Skip to content

Emoji logging sentry#2298

Merged
simo6529 merged 17 commits intomainfrom
emoji-logging-sentry
Apr 22, 2026
Merged

Emoji logging sentry#2298
simo6529 merged 17 commits intomainfrom
emoji-logging-sentry

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Apr 21, 2026

Summary by CodeRabbit

  • New Features

    • Websocket-aware reaction monitoring for real-time sync, reconciliation, and richer observability.
    • Centralized reaction error messaging for clearer, consistent toasts across reaction flows.
  • Bug Fixes

    • More consistent toast notifications and rollback behavior for failed reactions (auth, rate limits, service unavailable).
    • Improved detection and reporting of out-of-order or superseded reaction events; reduced duplicate error reports.
  • Chores

    • API/config: single winning_threshold replaces prior range-based threshold.

Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 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

Walkthrough

Adds structured API error handling and end-to-end monitoring for drop reactions: new monitoring module, hook and component integrations that record mutation lifecycle and reconcile websocket updates, API changes to surface structured errors, OpenAPI/helpers threshold shape updates, and extensive tests exercising these flows.

Changes

Cohort / File(s) Summary
Monitoring infra
utils/monitoring/dropReactionMonitoring.ts
New module implementing reaction mutation lifecycle tracking, mutation ID generation, dedupe window, Sentry breadcrumb/exception emission, realtime reconciliation, and a test reset helper.
Hook implementation
hooks/drops/useDropReaction.ts, __tests__/hooks/useDropReaction.test.ts
Hook signature changed to accept options ({ source, onSuccess }), builds/uses mutation contexts from monitoring, records optimistic/request/success/failure/rollback events, uses errorMode: "structured", and delegates error messages to getReactionErrorMessage. Tests added/updated.
Component integrations
components/waves/drops/WaveDropReactions.tsx, components/waves/drops/WaveDropActionsQuickReact.tsx, components/waves/drops/WaveDropActionsAddReaction.tsx, __tests__/components/waves/drops/*.test.tsx
Components updated to pass source/onSuccess to hook; WaveDropReactions now reads useWebsocketStatus(), creates mutation contexts via beginReactionMutation, instruments lifecycle events, and centralizes error-message resolution. Tests mock Sentry/websocket and assert structured error handling.
Error utilities
components/waves/drops/reaction-utils.ts, __tests__/components/waves/drops/reaction-utils.test.ts
Added exported getReactionErrorMessage(error, fallback) with helpers to parse structured response bodies/statusText and fallback rules; tests cover extraction and fallback behaviors.
API surface
services/api/common-api.ts, __tests__/services/common-api.test.ts
Structured API error shape extended to include optional statusText; createStructuredApiError/handleApiError pass statusText; commonApiDelete gains optional errorMode and executor forwards errorMode. Tests updated for whitespace/statusText cases.
Realtime updater
contexts/wave/hooks/useWaveRealtimeUpdater.ts
Records realtime reconciliation events (recordReactionRealtimeReconciliation) when processing incoming drop-reaction updates during reconciliation.
OpenAPI + helpers
openapi.yaml, helpers/waves/create-wave.helpers.ts, helpers/waves/waves.helpers.ts, __tests__/helpers/waves/*.test.ts
Replaced winning_thresholds with single winning_threshold (nullable integer) in OpenAPI and helper payloads; updated helpers/tests to produce/consume the new single-threshold shape and added decision-tracking fields in OpenAPI.
Tests & monitoring tests
__tests__/utils/monitoring/dropReactionMonitoring.test.ts, plus many reaction-related tests
New/expanded tests validating monitoring breadcrumbs, exception capture/deduping, out-of-order reconciliation, structured error parsing and toast behavior across components/hooks.

Sequence Diagram(s)

sequenceDiagram
  participant UI as Client UI
  participant Hook as useDropReaction
  participant Mon as dropReactionMonitoring
  participant API as commonApiPost/Delete
  participant WS as WebSocketStatus
  participant Sentry as Sentry

  UI->>Hook: click reaction (intendedReaction)
  Hook->>WS: read websocket status
  Hook->>Mon: beginReactionMutation(params)
  Mon-->>Hook: mutationContext
  Hook->>Mon: recordReactionOptimisticApplied
  Hook->>API: POST/DELETE (errorMode: "structured")
  Note over API,Mon: request in-flight
  API-->>Hook: success
  Hook->>Mon: recordReactionRequestSucceeded
  Hook->>UI: finalize / call onSuccess
  API-->>Hook: OR structured error
  Hook->>Mon: recordReactionRequestFailed(error)
  Hook->>Mon: recordReactionRollbackApplied
  Mon->>Sentry: addBreadcrumb / maybe captureException
  WS->>Mon: realtime update arrives
  Mon->>Mon: reconcile server state vs latest mutation
  Mon->>Sentry: emit reconciliation breadcrumb / capture if anomaly
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ragnep

Poem

🐇 "I hopped through lines with whiskered zest,

Tracked every emoji, east to west,
Breadcrumbs, webs, and tiny toasts,
Rollback flutters and reconciles most,
I chew code carrots — reactions now rest."

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Title check ❓ Inconclusive The title 'Emoji logging sentry' is vague and does not clearly convey the main purpose of the changeset. While the PR involves Sentry integration for reaction monitoring, the term 'Emoji logging' is ambiguous and non-descriptive. Consider revising the title to be more specific and descriptive, such as 'Add drop reaction monitoring with Sentry breadcrumbs and error handling' or 'Implement structured error handling and mutation tracking for wave reactions'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch emoji-logging-sentry

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.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 807ffc7aba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread utils/monitoring/dropReactionMonitoring.ts
Copy link
Copy Markdown

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
__tests__/components/waves/drops/WaveDropReactions.test.tsx (1)

280-292: ⚠️ Potential issue | 🟡 Minor

Assert that chip reactions request structured errors.

The new failure test would still pass if the component stopped passing errorMode: "structured". Update the API-call assertions so the structured-error contract is covered.

💚 Proposed test update
-    expect(commonApi.commonApiPost).toHaveBeenCalledWith({
-      endpoint: "drops/test-drop/reaction",
-      body: { reaction: ":gm:" },
-    });
+    expect(commonApi.commonApiPost).toHaveBeenCalledWith(
+      expect.objectContaining({
+        endpoint: "drops/test-drop/reaction",
+        body: { reaction: ":gm:" },
+        errorMode: "structured",
+      })
+    );
@@
-    expect(commonApi.commonApiDelete).toHaveBeenCalledWith({
-      endpoint: "drops/test-drop/reaction",
-    });
+    expect(commonApi.commonApiDelete).toHaveBeenCalledWith(
+      expect.objectContaining({
+        endpoint: "drops/test-drop/reaction",
+        errorMode: "structured",
+      })
+    );

Also applies to: 295-335

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/components/waves/drops/WaveDropReactions.test.tsx` around lines 280
- 292, Update the test assertions to verify the structured-error contract by
expecting the API calls include errorMode: "structured"; specifically, modify
the expect for commonApi.commonApiPost (the call that posts to endpoint
"drops/test-drop/reaction" with body { reaction: ":gm:" }) to include errorMode:
"structured" in the argument object, and likewise update the expect for
commonApi.commonApiDelete (the call that deletes at endpoint
"drops/test-drop/reaction") to assert it was called with an object containing
errorMode: "structured".
🧹 Nitpick comments (1)
__tests__/hooks/useDropReaction.test.ts (1)

65-118: Consider broadening test coverage for useDropReaction.

The suite currently asserts a single error-path scenario. Given the hook orchestrates optimistic updates, monitoring calls, and two API endpoints, consider also covering:

  • The remove path (commonApiDelete called with errorMode: "structured" when reactionCode === contextProfileContext.reaction).
  • Successful execution: onSuccess is invoked and recordReactionRequestSucceeded is called with the mutation context from beginReactionMutation.
  • Short-circuit when drop.id starts with "temp-" (canReact === false).
  • Fallback error message when the rejection does not carry a usable message.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/hooks/useDropReaction.test.ts` around lines 65 - 118, Add
additional tests for useDropReaction: (1) test the remove path by calling
result.current.react with a reaction equal to contextProfileContext.reaction and
assert commonApi.commonApiDelete is called with errorMode: "structured"; (2)
test successful execution by mocking commonApi.commonApiPost to resolve,
invoking react, and asserting onSuccess path behavior including that
recordReactionRequestSucceeded is called with the mutation context returned from
beginReactionMutation and that optimistic update/monitoring hooks are invoked;
(3) test the short-circuit behavior when drop.id starts with "temp-" by setting
mockDrop.id = "temp-..." and asserting canReact is false and no API call is
made; (4) test the fallback error message by rejecting the API promise with a
value that has no usable message and asserting setToast is called with a generic
error message—use the existing symbols useDropReaction, result.current.react,
commonApi.commonApiPost, commonApi.commonApiDelete, beginReactionMutation,
recordReactionRequestSucceeded, canReact, and setToastMock to locate and assert
the behaviors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hooks/drops/useDropReaction.ts`:
- Around line 183-202: The current try block calls onSuccess?.() so if the
consumer throws, our catch treats a successful API call as a failure; move the
onSuccess call out of the try so consumer errors don't trigger
recordReactionRequestFailed/setToast/recordReactionRollbackApplied. Concretely,
in useDropReaction.ts around the code using recordReactionRequestSucceeded,
recordReactionRequestFailed, rollbackRef.current and
recordReactionRollbackApplied: set a local success flag (e.g., let succeeded =
false), set succeeded = true immediately after
recordReactionRequestSucceeded(...) and clearing rollbackRef, then after the
try/catch (only when succeeded) call onSuccess?.() inside its own try/catch if
you want to swallow consumer errors; ensure rollbackRef.current is only
read/cleared inside the catch as before so rollbacks remain correct.

In `@utils/monitoring/dropReactionMonitoring.ts`:
- Around line 581-583: The current guard uses strict null checks so undefined
apiFailedAt causes an unintended early return; change the checks to
null-coalescing equality checks that treat undefined the same as null (e.g.,
replace "context.apiFailedAt !== null || context.apiSucceededAt === null" with
"context.apiFailedAt != null || context.apiSucceededAt == null") so that
recordReactionRequestSucceeded() setting apiSucceededAt lets reconciliation
mismatches be reported; update this condition in dropReactionMonitoring.ts
around the code that references context.apiFailedAt and context.apiSucceededAt
(the block that runs after recordReactionRequestSucceeded()).

---

Outside diff comments:
In `@__tests__/components/waves/drops/WaveDropReactions.test.tsx`:
- Around line 280-292: Update the test assertions to verify the structured-error
contract by expecting the API calls include errorMode: "structured";
specifically, modify the expect for commonApi.commonApiPost (the call that posts
to endpoint "drops/test-drop/reaction" with body { reaction: ":gm:" }) to
include errorMode: "structured" in the argument object, and likewise update the
expect for commonApi.commonApiDelete (the call that deletes at endpoint
"drops/test-drop/reaction") to assert it was called with an object containing
errorMode: "structured".

---

Nitpick comments:
In `@__tests__/hooks/useDropReaction.test.ts`:
- Around line 65-118: Add additional tests for useDropReaction: (1) test the
remove path by calling result.current.react with a reaction equal to
contextProfileContext.reaction and assert commonApi.commonApiDelete is called
with errorMode: "structured"; (2) test successful execution by mocking
commonApi.commonApiPost to resolve, invoking react, and asserting onSuccess path
behavior including that recordReactionRequestSucceeded is called with the
mutation context returned from beginReactionMutation and that optimistic
update/monitoring hooks are invoked; (3) test the short-circuit behavior when
drop.id starts with "temp-" by setting mockDrop.id = "temp-..." and asserting
canReact is false and no API call is made; (4) test the fallback error message
by rejecting the API promise with a value that has no usable message and
asserting setToast is called with a generic error message—use the existing
symbols useDropReaction, result.current.react, commonApi.commonApiPost,
commonApi.commonApiDelete, beginReactionMutation,
recordReactionRequestSucceeded, canReact, and setToastMock to locate and assert
the behaviors.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 10b0d955-c90c-4a95-b7d7-9959613e720b

📥 Commits

Reviewing files that changed from the base of the PR and between 057e2c7 and 807ffc7.

📒 Files selected for processing (12)
  • __tests__/components/waves/drops/WaveDropActionsAddReaction.test.tsx
  • __tests__/components/waves/drops/WaveDropReactions.test.tsx
  • __tests__/hooks/useDropReaction.test.ts
  • __tests__/utils/monitoring/dropReactionMonitoring.test.ts
  • components/waves/drops/WaveDropActionsAddReaction.tsx
  • components/waves/drops/WaveDropActionsQuickReact.tsx
  • components/waves/drops/WaveDropReactions.tsx
  • components/waves/drops/reaction-utils.ts
  • contexts/wave/hooks/useWaveRealtimeUpdater.ts
  • hooks/drops/useDropReaction.ts
  • services/api/common-api.ts
  • utils/monitoring/dropReactionMonitoring.ts

Comment thread hooks/drops/useDropReaction.ts
Comment thread utils/monitoring/dropReactionMonitoring.ts
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
components/waves/drops/reaction-utils.ts (1)

193-206: Minor: redundant case null in switch.

case null and default both return null; the explicit null case is dead given the default. Also, a lookup table tends to read a bit better here:

Proposed simplification
-const getEmptyStructuredReactionStatusMessage = (
-  status: number | null
-): string | null => {
-  switch (status) {
-    case 401:
-      return "Unauthorized";
-    case 429:
-      return "Too Many Requests";
-    case null:
-      return null;
-    default:
-      return null;
-  }
-};
+const EMPTY_BODY_STATUS_MESSAGES: Record<number, string> = {
+  401: "Unauthorized",
+  429: "Too Many Requests",
+};
+
+const getEmptyStructuredReactionStatusMessage = (
+  status: number | null
+): string | null => (status !== null ? EMPTY_BODY_STATUS_MESSAGES[status] ?? null : null);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/drops/reaction-utils.ts` around lines 193 - 206, The switch
in getEmptyStructuredReactionStatusMessage contains a redundant case null;
replace the switch with a small lookup (e.g., a statusMap object keyed by
numeric codes) that maps 401 -> "Unauthorized" and 429 -> "Too Many Requests"
and returns null for anything else, and delete the explicit case null branch so
the function returns map[status] ?? null (ensure the function signature still
accepts number | null and returns null for null).
__tests__/components/waves/drops/WaveDropReactions.test.tsx (3)

31-48: Sentry/websocket mocks look good; consider jest.Mocked over any.

The withScope callback argument is typed any — prefer a minimal scope type (or Partial<import("@sentry/nextjs").Scope>) to keep lint noise down and catch drift if the scope surface being exercised changes. Non-blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/components/waves/drops/WaveDropReactions.test.tsx` around lines 31
- 48, The withScope mock currently types its callback parameter as any; change
that to a minimal typed scope to reduce lint noise and catch API drift by using
Partial<import("@sentry/nextjs").Scope> (or jest.Mocked<Scope>) for the callback
argument in the jest.mock of "@sentry/nextjs" (the withScope mock) so the mocked
scope object keys (setLevel, setFingerprint, setTag, setExtras) match the
expected Scope shape; no changes needed to the useWebsocketStatus mock.

319-409: Good coverage for structured error → toast mapping.

Both the JSON-body (Unauthorized) and empty-body-with-statusText (Not Found) paths are exercised end-to-end through the UI. One small suggestion: add an assertion that the optimistic update was rolled back (rollback was called) on failure, since that's a likely regression surface when the error-handling path changes. Not essential for this PR.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/components/waves/drops/WaveDropReactions.test.tsx` around lines 319
- 409, Add an assertion after the waitFor in each failing-chip test to verify
the optimistic rollback was invoked: locate the test cases in
WaveDropReactions.test.tsx (the two it(...) blocks that mock
commonApi.commonApiPost rejection) and add expect(rollback).toHaveBeenCalled();
(or the actual mocked rollback function name used in the test harness) after the
existing setToastMock assertion so the test ensures the optimistic update is
rolled back on error.

70-92: Module-scoped setToastMock — confirm isolation across tests.

setToastMock is declared at module scope but jest.clearAllMocks() in beforeEach (line 137) clears its call history, so assertions remain isolated. 👍

However, createStructuredReactionError is duplicated verbatim with __tests__/components/waves/drops/reaction-utils.test.ts. Extract to a shared test helper to avoid drift when the structured-error shape evolves (e.g., if response.headers is added).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/components/waves/drops/WaveDropReactions.test.tsx` around lines 70
- 92, The helper createStructuredReactionError is duplicated; extract it (and
the module-scoped setToastMock if needed) into a shared test helper module
(e.g., tests/helpers/reactionTestUtils.ts), export createStructuredReactionError
and setToastMock, and then replace the inline definitions in
WaveDropReactions.test.tsx and reaction-utils.test.ts with imports from that
helper; update the tests to import { createStructuredReactionError, setToastMock
} and remove the duplicate declarations so any future shape changes (e.g.,
response.headers) are made in one place.
__tests__/components/waves/drops/reaction-utils.test.ts (1)

3-24: Duplicate createStructuredReactionError helper.

This exact helper is also defined in __tests__/components/waves/drops/WaveDropReactions.test.tsx (and likely in the reaction hook/component tests referenced by the summary). Consider extracting it to a shared test utility (e.g., __tests__/test-utils/structured-errors.ts) to keep the structured-error shape in one place as the error contract evolves.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/components/waves/drops/reaction-utils.test.ts` around lines 3 - 24,
Duplicate helper createStructuredReactionError is defined in multiple tests;
extract it into a single shared test utility (e.g., structured-errors.ts) and
replace the inline definitions with imports. Move the factory function signature
and implementation exactly as-is into the shared module, export it (named export
createStructuredReactionError), then update tests (those containing
createStructuredReactionError) to import { createStructuredReactionError } from
that utility and remove the duplicated definitions so the structured-error shape
is centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/waves/drops/reaction-utils.ts`:
- Around line 142-167: getStructuredReactionBodyMessage currently returns
whatever getDetailsFirstMessage(bodyRecord["details"]) yields, which can leave a
parseable-but-unknown-shaped object treated as a “structured body” and prevent
status/statusText fallbacks; change getStructuredReactionBodyMessage so that
after checking "error" and "message" it calls getDetailsFirstMessage and only
returns that value if it is a non-empty string, otherwise explicitly return null
— this makes parseable-but-no-known-fields be treated the same as no structured
body and aligns with how hasNoStructuredReactionBody/commonApiPost expect empty
results.

---

Nitpick comments:
In `@__tests__/components/waves/drops/reaction-utils.test.ts`:
- Around line 3-24: Duplicate helper createStructuredReactionError is defined in
multiple tests; extract it into a single shared test utility (e.g.,
structured-errors.ts) and replace the inline definitions with imports. Move the
factory function signature and implementation exactly as-is into the shared
module, export it (named export createStructuredReactionError), then update
tests (those containing createStructuredReactionError) to import {
createStructuredReactionError } from that utility and remove the duplicated
definitions so the structured-error shape is centralized.

In `@__tests__/components/waves/drops/WaveDropReactions.test.tsx`:
- Around line 31-48: The withScope mock currently types its callback parameter
as any; change that to a minimal typed scope to reduce lint noise and catch API
drift by using Partial<import("@sentry/nextjs").Scope> (or jest.Mocked<Scope>)
for the callback argument in the jest.mock of "@sentry/nextjs" (the withScope
mock) so the mocked scope object keys (setLevel, setFingerprint, setTag,
setExtras) match the expected Scope shape; no changes needed to the
useWebsocketStatus mock.
- Around line 319-409: Add an assertion after the waitFor in each failing-chip
test to verify the optimistic rollback was invoked: locate the test cases in
WaveDropReactions.test.tsx (the two it(...) blocks that mock
commonApi.commonApiPost rejection) and add expect(rollback).toHaveBeenCalled();
(or the actual mocked rollback function name used in the test harness) after the
existing setToastMock assertion so the test ensures the optimistic update is
rolled back on error.
- Around line 70-92: The helper createStructuredReactionError is duplicated;
extract it (and the module-scoped setToastMock if needed) into a shared test
helper module (e.g., tests/helpers/reactionTestUtils.ts), export
createStructuredReactionError and setToastMock, and then replace the inline
definitions in WaveDropReactions.test.tsx and reaction-utils.test.ts with
imports from that helper; update the tests to import {
createStructuredReactionError, setToastMock } and remove the duplicate
declarations so any future shape changes (e.g., response.headers) are made in
one place.

In `@components/waves/drops/reaction-utils.ts`:
- Around line 193-206: The switch in getEmptyStructuredReactionStatusMessage
contains a redundant case null; replace the switch with a small lookup (e.g., a
statusMap object keyed by numeric codes) that maps 401 -> "Unauthorized" and 429
-> "Too Many Requests" and returns null for anything else, and delete the
explicit case null branch so the function returns map[status] ?? null (ensure
the function signature still accepts number | null and returns null for null).
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac2d2e26-1a4a-4198-a6f4-a5e4ba897aec

📥 Commits

Reviewing files that changed from the base of the PR and between 807ffc7 and 42bc3c4.

📒 Files selected for processing (6)
  • __tests__/components/waves/drops/WaveDropReactions.test.tsx
  • __tests__/components/waves/drops/reaction-utils.test.ts
  • __tests__/hooks/useDropReaction.test.ts
  • __tests__/services/common-api.test.ts
  • components/waves/drops/reaction-utils.ts
  • services/api/common-api.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • services/api/common-api.ts
  • tests/hooks/useDropReaction.test.ts

Comment thread components/waves/drops/reaction-utils.ts
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@simo6529
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

✅ Actions performed

Review triggered.

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

Copy link
Copy Markdown

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
openapi.yaml (1)

11638-11709: ⚠️ Potential issue | 🔴 Critical

Missing required ApiWaveConfig fields in wave response mappers and request builders.

The helper functions constructing ApiWaveConfig objects are missing several required fields now mandated by the schema:

  • waves.helpers.ts (line 63–73): Response mapping omits total_no_of_decisions, no_of_decisions_done, no_of_decisions_left, authenticated_user_eligible_for_admin, and next_decision_time
  • create-wave.helpers.ts (line 401–423): Request builder omits the same fields

These fields are marked as required in openapi.yaml (lines 11641–11652) and included in the generated ApiWaveConfig model, but absence from these mappers will cause serialization gaps and violate the schema contract expected by generated clients.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openapi.yaml` around lines 11638 - 11709, The ApiWaveConfig mapping functions
in waves.helpers.ts (response mapper) and create-wave.helpers.ts (request
builder) are missing required fields from the OpenAPI schema; update the
functions that build ApiWaveConfig objects to include total_no_of_decisions,
no_of_decisions_done, no_of_decisions_left,
authenticated_user_eligible_for_admin, and next_decision_time (use the
corresponding source properties from the domain object or set explicit
null/defaults if not present), ensuring the object returned by the response
mapper (where ApiWaveConfig is assembled) and the request builder (where the
payload is assembled) include these exact property names so the generated
ApiWaveConfig model matches the schema.
🧹 Nitpick comments (2)
__tests__/services/common-api.test.ts (1)

119-366: LGTM — thorough coverage of the whitespace/precedence edge cases.

The new tests exercise all the interesting branches of handleApiError: whitespace-only raw body (structured + legacy), whitespace-only error field falling back to statusText, message preferred when error is whitespace, and details[0].message preferred when both higher-priority fields are whitespace. Assertions on response.statusText, response.body, and error.message together pin down the contract well.

Optional nit: there's noticeable duplication in the error type-cast shape and the try/catch-and-rethrow-into-let error pattern across the five structured-error tests. Extracting a small helper like catchStructuredError(fn) returning the typed error would shrink each test substantially — worth considering as a follow-up, not a blocker.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/services/common-api.test.ts` around lines 119 - 366, The tests
duplicate the same try/catch-and-typed-error pattern across multiple cases;
extract a helper (e.g., catchStructuredError) that accepts a call (or the
arguments for commonApiPost) and returns the caught error typed with { message:
string; response: { status: number; headers: Headers; statusText?: string;
body?: unknown } }; replace each repeated try/catch block with awaiting
catchStructuredError(() => commonApiPost({...})) (or
catchStructuredError({endpoint:"e",body:{},errorMode:"structured"}) if you
prefer passing args) and update assertions to use the returned error; keep the
helper local to the test file so other tests are unchanged.
components/waves/drops/reaction-utils.ts (1)

244-282: Optional: flatten getReactionErrorMessage to address SonarCloud cognitive-complexity warning.

SonarCloud flags cognitive complexity 22 (limit 15) at line 247. Extracting the "empty-body fallback chain" into a helper and using early returns brings this well under the threshold without behavior change.

♻️ Proposed refactor
+const getEmptyBodyFallback = (
+  error: StructuredReactionError
+): string | null =>
+  getEmptyStructuredReactionStatusMessage(getStructuredReactionStatus(error)) ??
+  getEmptyStructuredReactionStatusText(error) ??
+  getEmptyStructuredReactionFallbackMessage(error);
+
 export const getReactionErrorMessage = (
   error: unknown,
   fallback: string
 ): string => {
-  if (error !== null && typeof error === "object") {
-    const structuredError = error as StructuredReactionError;
-    const structuredResponse = structuredError.response;
-    if (structuredResponse !== undefined) {
-      const structuredBody = structuredResponse.body;
-      const safeMessage = getStructuredReactionBodyMessage(structuredBody);
-      if (safeMessage) {
-        return safeMessage;
-      }
-
-      if (hasNoStructuredReactionBody(structuredBody)) {
-        const statusMessage = getEmptyStructuredReactionStatusMessage(
-          getStructuredReactionStatus(structuredError)
-        );
-        if (statusMessage) {
-          return statusMessage;
-        }
-
-        const statusText =
-          getEmptyStructuredReactionStatusText(structuredError);
-        if (statusText) {
-          return statusText;
-        }
-
-        const fallbackMessage =
-          getEmptyStructuredReactionFallbackMessage(structuredError);
-        if (fallbackMessage) {
-          return fallbackMessage;
-        }
-      }
-    }
-  }
-
-  return fallback;
+  if (error === null || typeof error !== "object") {
+    return fallback;
+  }
+  const structuredError = error as StructuredReactionError;
+  const structuredResponse = structuredError.response;
+  if (structuredResponse === undefined) {
+    return fallback;
+  }
+  const safeMessage = getStructuredReactionBodyMessage(structuredResponse.body);
+  if (safeMessage) {
+    return safeMessage;
+  }
+  if (!hasNoStructuredReactionBody(structuredResponse.body)) {
+    return fallback;
+  }
+  return getEmptyBodyFallback(structuredError) ?? fallback;
 };

Also minor: in getEmptyStructuredReactionStatusMessage (Lines 198-208) the explicit case null is redundant given default also returns null — you can drop it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/waves/drops/reaction-utils.ts` around lines 244 - 282,
getReactionErrorMessage has high cognitive complexity due to nested checks and
an "empty-body fallback chain"; extract the block that handles
hasNoStructuredReactionBody(structuredBody) (the calls to
getEmptyStructuredReactionStatusMessage, getEmptyStructuredReactionStatusText,
and getEmptyStructuredReactionFallbackMessage) into a small helper (e.g.,
resolveEmptyStructuredReactionFallback(structuredError): string | null) and
refactor getReactionErrorMessage to use early returns for the
structuredResponse/structuredBody/safeMessage checks, delegating the fallback
chain to that helper to reduce nesting and complexity; optionally remove the
redundant explicit "case null" in getEmptyStructuredReactionStatusMessage since
default already returns null.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@helpers/waves/create-wave.helpers.ts`:
- Around line 401-405: The current wave creation sets wave.winning_threshold to
config.approval.thresholdTimeMs unconditionally; change this so that
winning_threshold is null unless the wave type (config.overview.type or the
created wave.type) equals 'APPROVE' — i.e., set wave.winning_threshold =
config.approval.thresholdTimeMs only when config.overview.type === 'APPROVE',
otherwise set it to null to match the schema for non-APPROVE waves.

In `@openapi.yaml`:
- Around line 8293-8297: The description for the max_winners property is
ambiguous about null semantics; update the OpenAPI description for max_winners
to explicitly state when null is allowed and what it means: for waves with type
== APPROVE, max_winners may be null to indicate unlimited or a non-null integer
to limit winners, and for non-APPROVE waves max_winners must be null/omitted.
Reference the max_winners property and the type value APPROVE when making this
change.

---

Outside diff comments:
In `@openapi.yaml`:
- Around line 11638-11709: The ApiWaveConfig mapping functions in
waves.helpers.ts (response mapper) and create-wave.helpers.ts (request builder)
are missing required fields from the OpenAPI schema; update the functions that
build ApiWaveConfig objects to include total_no_of_decisions,
no_of_decisions_done, no_of_decisions_left,
authenticated_user_eligible_for_admin, and next_decision_time (use the
corresponding source properties from the domain object or set explicit
null/defaults if not present), ensuring the object returned by the response
mapper (where ApiWaveConfig is assembled) and the request builder (where the
payload is assembled) include these exact property names so the generated
ApiWaveConfig model matches the schema.

---

Nitpick comments:
In `@__tests__/services/common-api.test.ts`:
- Around line 119-366: The tests duplicate the same try/catch-and-typed-error
pattern across multiple cases; extract a helper (e.g., catchStructuredError)
that accepts a call (or the arguments for commonApiPost) and returns the caught
error typed with { message: string; response: { status: number; headers:
Headers; statusText?: string; body?: unknown } }; replace each repeated
try/catch block with awaiting catchStructuredError(() => commonApiPost({...}))
(or catchStructuredError({endpoint:"e",body:{},errorMode:"structured"}) if you
prefer passing args) and update assertions to use the returned error; keep the
helper local to the test file so other tests are unchanged.

In `@components/waves/drops/reaction-utils.ts`:
- Around line 244-282: getReactionErrorMessage has high cognitive complexity due
to nested checks and an "empty-body fallback chain"; extract the block that
handles hasNoStructuredReactionBody(structuredBody) (the calls to
getEmptyStructuredReactionStatusMessage, getEmptyStructuredReactionStatusText,
and getEmptyStructuredReactionFallbackMessage) into a small helper (e.g.,
resolveEmptyStructuredReactionFallback(structuredError): string | null) and
refactor getReactionErrorMessage to use early returns for the
structuredResponse/structuredBody/safeMessage checks, delegating the fallback
chain to that helper to reduce nesting and complexity; optionally remove the
redundant explicit "case null" in getEmptyStructuredReactionStatusMessage since
default already returns null.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 753697d4-f4cf-42fb-ac9d-14fdca6635a0

📥 Commits

Reviewing files that changed from the base of the PR and between 42bc3c4 and 3fa8d2c.

⛔ Files ignored due to path filters (3)
  • generated/models/ApiCreateWaveConfig.ts is excluded by !**/generated/**
  • generated/models/ApiWaveConfig.ts is excluded by !**/generated/**
  • generated/models/ObjectSerializer.ts is excluded by !**/generated/**
📒 Files selected for processing (6)
  • __tests__/services/common-api.test.ts
  • components/waves/drops/reaction-utils.ts
  • helpers/waves/create-wave.helpers.ts
  • helpers/waves/waves.helpers.ts
  • openapi.yaml
  • services/api/common-api.ts

Comment thread helpers/waves/create-wave.helpers.ts
Comment thread openapi.yaml
Signed-off-by: Simo <simo@6529.io>
Copy link
Copy Markdown

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
__tests__/helpers/waves/create-wave.helpers.extra.test.ts (1)

160-260: LGTM — coverage for both Approve and non-Approve winning_threshold paths.

The updated Approve assertion on the scalar body.wave.winning_threshold === 60000 and the new Rank test asserting toBeNull() directly exercise the conditional added in create-wave.helpers.ts L406-409. This prevents regressions of the past review finding.

Nit (pre-existing, not introduced here): Lines 109, 160, and 211 define it(...) blocks at the top level, outside the describe("create-wave.helpers extra", ...) that closes at Line 108. They still execute, but grouping them under the describe would improve report readability. Feel free to defer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__tests__/helpers/waves/create-wave.helpers.extra.test.ts` around lines 160 -
260, Three test cases (the two new it(...) blocks checking winning_threshold and
another earlier it at line 109) are declared at the top-level instead of inside
the existing describe("create-wave.helpers extra", ...) which harms test
grouping; move the two new it(...) blocks into that describe (or wrap them and
the top-level it at line 109 in a new describe) so all tests for
create-wave.helpers.extra are grouped together, keeping the assertions using
getCreateNewWaveBody and ApiWaveType unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@utils/monitoring/dropReactionMonitoring.ts`:
- Around line 56-73: pruneState currently evicts mutationContextById and
latestMutationIdByDrop but never removes dropMutationSeqByDrop, causing
unbounded growth; update pruneState so that when you delete the
latestMutationIdByDrop entry for a drop (i.e., inside the branch where
latestMutationIdByDrop.get(context.dropId) === mutationId and you call
latestMutationIdByDrop.delete(context.dropId)), also call
dropMutationSeqByDrop.delete(context.dropId) so the per-drop sequence state is
pruned only when the last tracked mutation for that drop ages out (preserving
sequences while in-flight mutations exist).
- Around line 613-625: The fingerprint in captureReactionEvent currently uses
ANOMALY_REVERTED_AFTER_SUCCESS while the dedupe key, anomaly_kind tag, and
anomaly_kind extra use ANOMALY_OPTIMISTIC_REVERTED; update
captureReactionEvent's fingerprint to use ANOMALY_OPTIMISTIC_REVERTED (alongside
REACTION_FEATURE) so the Sentry grouping matches the anomaly_kind tags/extra,
and then remove the now-unused ANOMALY_REVERTED_AFTER_SUCCESS constant from the
module to avoid confusion.

---

Nitpick comments:
In `@__tests__/helpers/waves/create-wave.helpers.extra.test.ts`:
- Around line 160-260: Three test cases (the two new it(...) blocks checking
winning_threshold and another earlier it at line 109) are declared at the
top-level instead of inside the existing describe("create-wave.helpers extra",
...) which harms test grouping; move the two new it(...) blocks into that
describe (or wrap them and the top-level it at line 109 in a new describe) so
all tests for create-wave.helpers.extra are grouped together, keeping the
assertions using getCreateNewWaveBody and ApiWaveType unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93a3c003-34de-4301-b40b-0bf42dc1e121

📥 Commits

Reviewing files that changed from the base of the PR and between 3fa8d2c and a46bd8d.

📒 Files selected for processing (7)
  • __tests__/helpers/waves/create-wave.helpers.extra.test.ts
  • __tests__/helpers/waves/create-wave.helpers.test.ts
  • __tests__/hooks/useDropReaction.test.ts
  • __tests__/utils/monitoring/dropReactionMonitoring.test.ts
  • helpers/waves/create-wave.helpers.ts
  • hooks/drops/useDropReaction.ts
  • utils/monitoring/dropReactionMonitoring.ts
✅ Files skipped from review due to trivial changes (1)
  • tests/utils/monitoring/dropReactionMonitoring.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/hooks/useDropReaction.test.ts

Comment thread utils/monitoring/dropReactionMonitoring.ts
Comment thread utils/monitoring/dropReactionMonitoring.ts
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@simo6529
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

✅ Actions performed

Review triggered.

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

Copy link
Copy Markdown

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
utils/monitoring/dropReactionMonitoring.ts (2)

552-586: Reconciliation window is measured from startedAt, not from API completion — slow requests can silently miss mismatch detection.

At line 568 the 15 s window starts at context.startedAt. If the API takes ~12–14 s (slow network, cold backend), a websocket reconciliation arriving even a second after the server ACK will be dropped because now - startedAt > RECONCILIATION_WINDOW_MS, so the optimistic_reverted anomaly will never be reported for exactly the class of user where it matters most.

Consider anchoring the window on apiSucceededAt ?? requestSentAt ?? startedAt so the window bounds "time since we expected the server's canonical state", not "time since the user clicked". Not critical, but worth reconsidering.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@utils/monitoring/dropReactionMonitoring.ts` around lines 552 - 586, The
current time-window check in recordReactionRealtimeReconciliation uses
context.startedAt to decide if a websocket reconciliation is stale; change it to
anchor the reconciliation window to the point we expect the server's canonical
state (e.g., use const anchor = context.apiSucceededAt ?? context.requestSentAt
?? context.startedAt) and replace the now - context.startedAt >
RECONCILIATION_WINDOW_MS check with now - anchor > RECONCILIATION_WINDOW_MS so
slow API responses don't cause missed anomaly detection; keep the other exits
(apiFailedAt/apiSucceededAt checks) intact.

46-47: Remove failureCaptured and supersededByMutationId from ReactionMutationContext — they are written but never read.

These fields are mutated at lines 319, 506, and 543 but never inspected anywhere in the codebase. A full repository search confirms no caller reads them. If they're not needed for future logging or analytics, remove them to reduce dead state in the context object.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@utils/monitoring/dropReactionMonitoring.ts` around lines 46 - 47, Remove the
dead state fields failureCaptured and supersededByMutationId from the
ReactionMutationContext type and delete all assignments that set those
properties (the writes at the three mutation sites that set
context.failureCaptured and context.supersededByMutationId). Update any
constructors or initializers that add these keys to the context so they are not
created, and run tests / a repo-wide search to ensure no reads remain; if any
future logging/analytics code needs similar data, add it back with a clear
usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/waves/drops/reaction-utils.ts`:
- Around line 125-140: The function getDetailsFirstMessage currently only
inspects details[0] and returns null if that item is blank or malformed; update
getDetailsFirstMessage to iterate over the details array (use
isNonEmptyUnknownArray(details) as the guard), examine each entry as an object
with an optional message property, and return the first non-empty trimmed string
found; if no entries yield a usable message return null. Ensure references to
getDetailsFirstMessage and isNonEmptyUnknownArray are preserved and remove the
single-index assumption (currently using firstDetail) in favor of a loop or
array.find approach.
- Around line 260-290: The code dereferences structuredResponse.body without
guarding for response === null and also skips status-only errors; update the
block that casts error to StructuredReactionError and replace the current
structuredResponse checks so you first test whether structuredError.response is
non-null (use structuredError.response != null) before accessing .body and
calling getStructuredReactionBodyMessage/hasNoStructuredReactionBody, and if
response is null/undefined fall back to treating the error as a status-only case
by calling getStructuredReactionStatus(structuredError) and then running
getEmptyStructuredReactionStatusMessage, getEmptyStructuredReactionStatusText,
and getEmptyStructuredReactionFallbackMessage in the same order; ensure you
reference StructuredReactionError, structuredResponse,
getStructuredReactionBodyMessage, hasNoStructuredReactionBody,
getStructuredReactionStatus, getEmptyStructuredReactionStatusMessage,
getEmptyStructuredReactionStatusText, and
getEmptyStructuredReactionFallbackMessage when making these guards and fallback
calls.

In `@helpers/waves/create-wave.helpers.ts`:
- Around line 324-334: The current check in the isRolling path only uses typeof
to validate dates.endDate, which allows NaN/Infinity; update the validation in
create-wave.helpers.ts to require Number.isFinite(dates.endDate) (instead of or
in addition to typeof) and throw the same error message if it fails, so that
calculateLastDecisionTime(dates.firstDecisionTime, dates.subsequentDecisions,
dates.endDate) always receives a finite millisecond timestamp; locate the check
around the isRolling logic and the `if (typeof dates.endDate !== "number")`
branch and replace it with a Number.isFinite validation.

---

Nitpick comments:
In `@utils/monitoring/dropReactionMonitoring.ts`:
- Around line 552-586: The current time-window check in
recordReactionRealtimeReconciliation uses context.startedAt to decide if a
websocket reconciliation is stale; change it to anchor the reconciliation window
to the point we expect the server's canonical state (e.g., use const anchor =
context.apiSucceededAt ?? context.requestSentAt ?? context.startedAt) and
replace the now - context.startedAt > RECONCILIATION_WINDOW_MS check with now -
anchor > RECONCILIATION_WINDOW_MS so slow API responses don't cause missed
anomaly detection; keep the other exits (apiFailedAt/apiSucceededAt checks)
intact.
- Around line 46-47: Remove the dead state fields failureCaptured and
supersededByMutationId from the ReactionMutationContext type and delete all
assignments that set those properties (the writes at the three mutation sites
that set context.failureCaptured and context.supersededByMutationId). Update any
constructors or initializers that add these keys to the context so they are not
created, and run tests / a repo-wide search to ensure no reads remain; if any
future logging/analytics code needs similar data, add it back with a clear
usage.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01cb9ee8-12e4-46bf-a90e-83560625f407

📥 Commits

Reviewing files that changed from the base of the PR and between a46bd8d and df78d58.

📒 Files selected for processing (6)
  • __tests__/components/waves/drops/reaction-utils.test.ts
  • __tests__/helpers/waves/create-wave.helpers.extra.test.ts
  • __tests__/utils/monitoring/dropReactionMonitoring.test.ts
  • components/waves/drops/reaction-utils.ts
  • helpers/waves/create-wave.helpers.ts
  • utils/monitoring/dropReactionMonitoring.ts
✅ Files skipped from review due to trivial changes (1)
  • tests/components/waves/drops/reaction-utils.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/helpers/waves/create-wave.helpers.extra.test.ts
  • tests/utils/monitoring/dropReactionMonitoring.test.ts

Comment thread components/waves/drops/reaction-utils.ts
Comment thread components/waves/drops/reaction-utils.ts
Comment thread helpers/waves/create-wave.helpers.ts
Comment thread utils/monitoring/dropReactionMonitoring.ts
Signed-off-by: Simo <simo@6529.io>
Signed-off-by: Simo <simo@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

@simo6529 simo6529 merged commit 14a04d3 into main Apr 22, 2026
8 checks passed
@simo6529 simo6529 deleted the emoji-logging-sentry branch April 22, 2026 11:06
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.

2 participants