Skip to content

feat(frontend): track cfs_sign Plausible event#13144

Draft
sbpublic wants to merge 7 commits into
mainfrom
feat/frontend/track-cfs-sign-event
Draft

feat(frontend): track cfs_sign Plausible event#13144
sbpublic wants to merge 7 commits into
mainfrom
feat/frontend/track-cfs-sign-event

Conversation

@sbpublic

Copy link
Copy Markdown
Collaborator

Motivation

A recent support incident: the chain-fusion signer could not sign for any user because OISY's backend ran out of TCycles on the cycles ledger. Every paid signer call attaches a payment, so when the backend account runs dry the signer returns a PaymentError and signing fails wallet-wide. Today this class of incident is invisible on our dashboards until support tickets arrive.

This PR (the first of two from the spec) adds the observability half: a cfs_sign Plausible event on every paid chain-fusion-signer call, so the next outage is visible immediately — broken down by method, and flagged result_error_severity = blocker when it is the backend-out-of-cycles case.

It is a pure addition with no behaviour change. The follow-up PR adds the user-facing friendly toast.

Spec: docs/ai/spec-driven-development/specs/2026-06-18-impr-cfs-signing-error-toast-and-cfs-sign-event.md (Part B).

Changes

  • Add PLAUSIBLE_EVENTS.CFS_SIGN, and the PLAUSIBLE_EVENT_RESULT_SEVERITIES and PLAUSIBLE_EVENT_SUBCONTEXT_CFS enums.
  • Add isSignerCanisterPaymentError guard in signer.errors.ts — every PaymentError variant is treated as "signer temporarily unavailable" (reused by the follow-up toast PR).
  • Add trackCfsSign + withCfsSignTracking in analytics.services.ts. The wrapper times the call, emits on both success and error, sets severity blocker for the payment outage / critical otherwise, and always re-throws so it never swallows the error or interrupts a send.
  • Instrument every exported function in signer.api.ts (the single chokepoint all paid signer calls pass through) with the matching method subcontext. Covers address/balance reads and all signing operations.
  • Derive token_network (eth/btc/sol) from the method prefix; omitted for chain-agnostic generic ECDSA.
  • Document the event in PRODUCT.md (Analytics) and commit the spec into the repo.

Tests

  • signer.errors.spec.ts: isSignerCanisterPaymentError returns true for each PaymentError variant, false for CanisterInternalError / plain Error / nullish / string.
  • analytics.service.spec.ts: success emits cfs_sign with method/status/duration/token_network; error emits result_error* with blocker for a payment error and critical otherwise; token_network derivation per prefix; withCfsSignTracking re-throws.
  • npm run format, eslint --max-warnings 0, and the touched unit specs (37 tests) pass. svelte-check reports no problems in the changed files.

Divergence from the spec

  • withCfsSignTracking takes a single object param { method, fn } instead of the spec's positional (method, fn) signature, to satisfy the repo's local-rules/prefer-object-params lint rule (enforced under --max-warnings 0). Call sites updated accordingly.
  • The isSignerCanisterPaymentError guard (spec Part A1) is introduced here in PR1 rather than the toast PR, because trackCfsSign needs it; the toast PR will reuse it.

🤖 Generated with Claude Code — model: Claude Opus 4.8 (claude-opus-4-8)

sbpublic and others added 5 commits June 18, 2026 16:11
Add CFS_SIGN to PLAUSIBLE_EVENTS plus the PLAUSIBLE_EVENT_RESULT_SEVERITIES
and PLAUSIBLE_EVENT_SUBCONTEXT_CFS enums used to describe each paid
chain-fusion-signer call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Detect the chain-fusion signer's payment outage (backend out of cycles)
from a thrown error. Every PaymentError variant maps to signer-unavailable,
not a user error. Reused by analytics now and the friendly toast later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
trackCfsSign builds the cfs_sign payload (method, status, duration,
token_network, and on error the mapped message, raw text and severity:
blocker for the signer payment outage, critical otherwise).
withCfsSignTracking times a paid signer call and emits on both success
and error, always re-throwing so it never swallows the underlying error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap each exported signer.api function in withCfsSignTracking at the single
API chokepoint, so address/balance reads and every signing operation emit a
cfs_sign event tagged with the called method.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the cfs_sign subsection to PRODUCT.md Analytics and commit the
spec-driven-development spec into the repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sbpublic

Copy link
Copy Markdown
Collaborator Author

Review — ✅ LGTM (observability half)

Faithful to Part B of the spec, pure addition, no behaviour change.

  • All 11 paid signer calls in signer.api.ts wrapped via withCfsSignTracking with the correct PLAUSIBLE_EVENT_SUBCONTEXT_CFS method.
  • CFS_SIGN, PLAUSIBLE_EVENT_RESULT_SEVERITIES, PLAUSIBLE_EVENT_SUBCONTEXT_CFS enums added cleanly.
  • withCfsSignTracking fires on both success and error, measures duration, and always re-throws — tracking never swallows the error.
  • isSignerCanisterPaymentError guard added (reused by fix(frontend): friendly toast when chain-fusion signer is unavailable #13145).
  • Bonus: implemented the optional token_network derivation from the method prefix.
  • Tests cover the error + analytics paths.

Two minor, non-blocking notes:

  • cfs_sign volume will be dominated by address/balance reads (they fire on load), and a startup-time outage will tag those reads blocker too. Intended per "all paid calls" — just ensure dashboards/alerts filter by event_subcontext so reads don't drown out signing ops.
  • result_error: (err as Error).message assumes an Error is thrown; fine for current signer errors, slightly fragile if a non-Error ever propagates.

@sbpublic

Copy link
Copy Markdown
Collaborator Author

Follow-up — final design (Part B)

Tracking design was refined since the review above — both good calls:

  • Dedicated cfs context (PLAUSIBLE_EVENT_CONTEXTS.CFS) instead of reusing signer, so chain-fusion-signer events are cleanly separable from the dApp-signer feature on dashboards (resolves the disambiguation note in my review).
  • New event_code on every event (success / cfs_payment_failed_backend_out_of_cycles / cfs_payment_failed_user_allowance_exhausted / cfs_generic_error), with classifyCfsSignError keeping event_code and result_error_severity in sync. "All payment failures" is now a one-property filter via the cfs_payment_failed_ prefix.

Still ✅ LGTM.

sbpublic and others added 2 commits June 19, 2026 11:32
…fs-sign-event

# Conflicts:
#	src/frontend/src/lib/enums/plausible.ts
#	src/frontend/src/lib/services/analytics.services.ts
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