SIGN IN SIGN UP
koala73 / worldmonitor UNCLAIMED

Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface

0 0 157 TypeScript

refactor(followedCountries): FREE_CAP throw → return discriminated union (#3915)

* refactor(followedCountries): FREE_CAP throw → return discriminated union

Convex Cloud's server-side auto-Sentry forwards every server throw —
including intentional `ConvexError({kind:...})` used for client signaling —
directly to our Sentry DSN, bypassing the browser `Sentry.init({...})`
config entirely (event tag `sdk.name=convex`, not `@sentry/browser`).

For high-volume expected business conditions like the free-tier
followed-countries cap, this produces noise from a path the client already
handles correctly (catch → discriminated-union → upgrade modal).

Follow the WM-established `userPreferences:CONFLICT` precedent: change the
server from throw-on-cap to return-on-cap, update the client to read the
return value. Eliminates the SOURCE so Convex auto-Sentry has nothing to
forward.

Other throws stay (UNAUTHENTICATED, INVALID_COUNTRY, shard missing) —
those ARE bugs and we WANT them in Sentry.

Client keeps the legacy catch block for the deploy-skew window between
server-refactor merge and Convex deploy completing. Safe to drop one
deploy cycle later; tracking via the `Legacy deploy-skew path` comment.

Supersedes #3914 (which added an inert `ignoreErrors` regex to
`src/main.ts`). Companion skill:
~/.claude/skills/convex-gotchas/reference/convex-autosentry-forwards-intentional-convexerror-throws.md

Resolves WORLDMONITOR-RV.

* test(convex): update FREE_CAP assertions for return path

Reviewer surfaced a real gap: the prior commit changed the server from
throw → return for FREE_CAP, but four Convex test scenarios still asserted
`rejects.toThrow(/FREE_CAP/)` against the mutation. Those would fulfill
under the new contract, failing the assertion.

Updated:
- Free user at cap: now `resolves.toEqual({ok:false, reason:'FREE_CAP', currentCount:3, limit:3})`
- Expired entitlement treated as free-tier: same return-shape assertion
- Concurrent cap-boundary (`Promise.all` two new follows on free user at 2/3):
  now `Promise.all` instead of `Promise.allSettled`; split results into
  `successes` and `capRefusals` discriminated-union groups
- "FREE_CAP rolls back all writes" → renamed to "FREE_CAP skips all writes"
  (the early return now happens BEFORE any insert/increment/patch, so no
  transaction rollback is needed — observable end state is the same)

All 449 convex tests pass (`npm run test:convex`).

Verifies WORLDMONITOR-RV fix on the server-side test surface.
E
Elie Habib committed
3c2e652b8167729439daa8c23efdf10be6fab00e
Parent: 349e4df
Committed by GitHub <noreply@github.com> on 5/26/2026, 5:33:53 PM