feat: engineering quality gates & developer experience (Epic #652)#670
Closed
christianahtemitope2005 wants to merge 23 commits into
Closed
Conversation
…ferral-rewards-656 feat(rewards): on-chain referral reward engine with anti-abuse (FinesseStudioLab#656)
) Register and claim now reflect the expected state the instant the user submits, then reconcile with chain truth on success or roll back on failure — so successful actions feel instant and failures recover cleanly. - useOptimisticAction hook (src/hooks): drives the idle → pending → success | error lifecycle. Applies an optimistic update, awaits the action, reconciles on success, and rolls back on failure. A synchronous in-flight latch guards against double-submit (a second click is ignored, never fires a second transaction). Rollback only runs if the optimistic update was actually applied. - errorMapping: add classifyError + mapError giving each failure a class (contract | wallet | network | rpc | validation | unknown) with distinct messaging — a network/RPC outage now reads differently from a contract revert, and contract reverts keep their precise per-code copy. - RegisterCampaign: status flips to "Registered" optimistically on submit; rolls back to the prior status on failure with a mapped error. - ClaimRewards: clears the input optimistically + shows a pending indicator; restores the amount on failure; reconciles the balance via onClaimSuccess. - TransactionStatus: new pending/success/error variant (backward compatible) so the pending state shows before a hash exists and errors render inline. Tests (src/hooks/useOptimisticAction.test.jsx, runs under the vitest include): optimistic-apply + reconcile, rollback on failure, no rollback when the optimistic step itself throws, double-submit guard, reset, and error classification (contract vs wallet vs network vs rpc messaging). A live register/claim E2E that forces a failure needs the docker-compose backend (the CI playwright run has none and skips the lifecycle suite), so rollback is covered here at the hook/integration level; the E2E can follow in that environment.
…ptimistic-register-claim feat(frontend): optimistic UI for register/claim with rollback (FinesseStudioLab#627)
…sseStudioLab#626) Replaces wasteful interval polling with a live SSE stream for campaign / participant state, while keeping polling as an automatic fallback. - lib/realtimeClient.js: dependency-free SSE subscription client. Parses JSON messages into { id, type, data }, de-duplicates by event id over a bounded window (idempotent against replays / out-of-order delivery), and reconnects with capped exponential backoff driven explicitly (so the UI can fall back to polling while disconnected). EventSource is injectable. - hooks/useRealtimeSubscription.js: React wrapper that opens/closes the subscription for a url while enabled and exposes the connection status; onEvent is read via a ref so re-renders don't churn the connection. - hooks/useCampaignLiveUpdates.js: drop-in augmentation of useCampaignPolling. While the stream is live, interval polling is paused; each event merges carried campaign fields into the cache (counts/claims update live) and triggers a refresh to reconcile with the authoritative API + on-chain state. On disconnect, polling resumes. With no stream configured it is pure polling, exactly as before (useCampaignPolling is left untouched). - config.js: getRealtimeUrl(campaignId) resolves VITE_REALTIME_URL or, when VITE_REALTIME_ENABLED=true, derives the SSE path; '' (default) → polling. - CampaignDetail.jsx: switched to useCampaignLiveUpdates (same returned shape; identical behaviour until a stream is configured). Tests (src/hooks, runs under the vitest include): client connect/dispatch, id de-dup, exponential-backoff reconnect, backoff reset on success, close() teardown, constructor guards; the subscription hook's connect/live/forward/ cleanup; and the composed hook's pure-polling fallback + go-live + reconcile. A live-update E2E needs the docker-compose backend with an SSE endpoint (the CI playwright run has none and skips the lifecycle suite), so live behaviour is covered here at the client/hook level; the E2E can follow in that env.
…ealtime-subscription feat(frontend): client-side real-time updates via SSE with polling fallback (FinesseStudioLab#626)
FinesseStudioLab#650) Implements the full observability & runtime-reliability epic: **RPC pool safety** - Add acquire/release concurrency gate to rpcPool.js (maxConcurrent=10, acquireTimeoutMs=5000). Waiters over the timeout receive a typed PoolSaturatedError (code POOL_SATURATED) so the error handler can return 503 instead of letting callers hang. - getStatus() exposes in_use/idle/waiting/healthy/unhealthy for Prometheus. **Request deadlines** - New backend/src/middleware/timeout.js: requestTimeout(ms) attaches an AbortSignal to req.signal, propagates client-disconnect aborts, and returns 504 REQUEST_TIMEOUT when the wall-clock deadline fires. - Wired globally in index.js (DEFAULT_REQUEST_TIMEOUT_MS=30000, overrideable via env REQUEST_TIMEOUT_MS). **Prometheus metrics** - /metrics now emits a full histogram for trivela_http_request_duration_ms (buckets: 50/100/200/500/1000/2000/5000/10000/30000/+Inf) enabling histogram_quantile p50/p95/p99 in Grafana and PromQL. - RPC pool gauges: trivela_rpc_pool_in_use, _idle, _waiting, _healthy, _unhealthy — consumed by RpcPoolSaturated and AllRpcEndpointsUnhealthy alerts. **Graceful shutdown** - SIGTERM/SIGINT handler in startServer(): stops accepting new connections, forces exit after SHUTDOWN_GRACE_MS (default 15 s), flushes OpenTelemetry traces via shutdownTracing(), then exits cleanly. **Prometheus alerting rules** (monitoring/alerting/alerting_rules.yml) - trivela_backend: HighBackendErrorRate (>5% for 5 m, critical), HighP95Latency (p95 > 1 s), BackendProcessRestart, BackendDown, AuthFailureSpike, AuthLockoutTriggered. - trivela_rpc: AllRpcEndpointsUnhealthy, RpcPoolSaturated, RPCHealthCheckDown. - trivela_indexer: IndexerLag (cursor stalled > 10 m). - trivela_contracts: ContractPaused, CampaignDBWriteErrors. - trivela_dlq: DLQGrowth. - trivela_operator: OperatorLowBalance (< 5 XLM). - trivela_canary: CanaryJourneyFailed, CanarySlowJourney (> 30 s). **promtool unit tests** (monitoring/alerting/alerting_rules_test.yml) - 10 test cases covering all primary alerts (both fire and no-fire paths). **Alertmanager** (monitoring/alertmanager.yml) - Routes critical→#trivela-critical + PagerDuty, contracts→#trivela-contracts, everything else→#trivela-alerts. Inhibition rules suppress child alerts when BackendDown or AllRpcEndpointsUnhealthy fires. **Grafana dashboards-as-code** (monitoring/dashboards/) - trivela-api.json: request rate, error rate, p50/p95/p99 latency, auth events, backend up/uptime. - trivela-rpc-pools.json: pool saturation gauges (in-use, waiting, idle), endpoint health time series. - Provisioning configs: grafana/provisioning/datasources/prometheus.yml and grafana/provisioning/dashboards/trivela.yml for zero-config Grafana startup. **Synthetic canary** (scripts/canary.mjs) - Five-step journey: health check → create canary campaign → credit claimant → verify stats → cleanup. Emits trivela_canary_success, _duration_seconds, _last_run_timestamp in Prometheus text format. - Designed for cron/every-5-min scheduling; CANARY_METRICS_FILE writes to a file for node-exporter textfile collector instead of stdout. **SLO definitions** (docs/SLO.md) - Availability (99.5% API, 99.9% reachability, 99.0% RPC), latency (p95 ≤ 1 s), indexer freshness (cursor advance every 10 m), pool saturation, canary, and operator balance SLOs. Error budget policy and measurement pointers. **CI** (.github/workflows/observability-ci.yml) - Runs promtool check rules + promtool test rules on every monitoring/ change. - Syntax-checks the canary script and dry-runs it with a 2 s timeout. - Runs backend unit tests scoped to the backend package via turbo.
…ilure
**Prettier formatting (3 files re-formatted)**
The format-check workflow runs prettier over backend/src/**/*.{js,json} and
**/*.{md,yaml,yml}. The following files written in the observability commit
did not match the repo's prettier style and needed reformatting:
- backend/src/index.js
- .github/workflows/observability-ci.yml
- docs/SLO.md
- monitoring/alerting/alerting_rules.yml
- monitoring/alerting/alerting_rules_test.yml
- monitoring/alertmanager.yml
All files now pass `prettier --check`.
**PromQL parse error in OperatorLowBalance rule**
The expression used `50_000_000` (JavaScript numeric separator syntax).
PromQL does not support underscores in numeric literals, causing:
bad number or duration syntax: "50" (at line 216)
Fix: change to `50000000` (plain integer).
**Canary JSDoc prematurely closed by `*/` in cron example**
Line 17 of scripts/canary.mjs contained:
* # */5 * * * * node scripts/canary.mjs ...
The `*/` in the cron expression closed the surrounding `/** ... */` block
comment, leaving the rest of the line as bare JS code. Node saw:
SyntaxError: Unexpected token '*'
Fix: rewrite the scheduling note to avoid `*/` inside the block comment.
`node --check scripts/canary.mjs` now passes.
The promtool unit tests failed for two reasons: - HighP95Latency never fired: its highest finite histogram bucket was le=1000, so histogram_quantile capped at 1000 and could never exceed the >1000ms threshold. Add a le=2000 bucket so p95 lands above the SLO. - Every firing exp_alerts entry omitted the job label and exp_annotations the rules actually emit, so promtool reported label/annotation mismatches. Add the job label (where present) and full exp_annotations to each. promtool test rules now passes (16 rules, all unit tests green).
…rvability-reliability-650 feat(observability): production-grade metrics, alerting, synthetic canary & SLOs (FinesseStudioLab#650)
…tudioLab#652) Implements all six work items from the Epic/consolidation issue: ## Coverage gates (JS + Rust) - Expanded vitest.config.js to cover all src/**/*.test.{js,jsx,ts,tsx} files (previously only src/hooks/**) and added @vitest/coverage-v8 with thresholds: lines 40%, functions 35%, branches 35%, statements 40%. - Added `test:coverage` script to frontend; CI runs it and uploads the lcov report as an artifact. Failures block the PR. - Added cargo-llvm-cov to contracts-ci.yml with a 60% line-coverage threshold. The Python snippet reads the JSON report and exits non-zero when the threshold isn't met. ## API contract tests - New backend/src/integration/openapi-contract.test.js validates live HTTP responses (success + error shapes) against the schemas declared in openapi.yaml using Ajv. Tests cover /health, /api/v1/campaigns (list, by-id, 404), POST create (201 + 422), and /api/v1/config. - Added ajv-formats to backend devDependencies and a `test:contract` script; backend-ci.yml runs it as a separate step. ## Deterministic test factories & seeders - backend/src/tests/factories.js — makeCampaign, makeCampaignInput, makeParticipant, makeApiKey, makeCampaigns with a resettable sequence counter so every call yields unique, predictable fixtures. - backend/src/tests/seed.js — idempotent SQLite seed script for local dev and devnet workflows. - frontend/src/tests/factories.js — mirrors API response shape for component-level unit tests; includes makeCampaignListResponse helper. ## Bundle budget + route-level code splitting - App.jsx: converted all non-landing routes to React.lazy() + Suspense so each page is a separate async chunk. - vite.config.js: manualChunks splits vendor-react, vendor-router, vendor-charts (recharts/d3), and vendor-stellar into cacheable chunks. - scripts/check-bundle-size.mjs: post-build gate that fails CI if any single JS chunk > 600 KB or total JS > 2 000 KB (uncompressed). - Added `check:bundle` to root package.json; frontend-ci.yml runs it immediately after the Vite build and before artifact upload. ## URL-synced filter/sort/search - Already implemented in Landing.jsx via useSearchParams (q, active, sortKey, page params); confirmed working, no further changes needed. ## Auto-published contract TypeScript bindings - New .github/workflows/release-bindings.yml triggers on vX.Y.Z tags, builds WASM, generates bindings via `npm run contracts:build-bindings`, computes SHA-256 hashes of both WASM files, writes metadata.json (version, generatedAt, wasmSha256, contractId), scaffolds the @trivela/contract-bindings package, and publishes to GitHub Packages. Closes FinesseStudioLab#652
…sue-637-expanded-contract-fuzzing Feat/issue 637 expanded contract fuzzing
- embed.test.js: auto-formatted by prettier (trailing commas, quote style) - embed.js: removed /* global window, document */ comment that triggered no-redeclare (they are already browser globals); added comment text to two empty catch blocks to satisfy the no-empty eslint rule
…udioLab#619) Add VAPID Web Push so subscribed users receive notifications for key events (credit available, ending soon, claim ready), with unsubscribe and dead-sub pruning. Backend: - push_subscriptions store (migration 013) + DAL, deduped per endpoint - webPushService: VAPID send-to-user fan-out, prunes subscriptions the push service reports gone (404/410); no-op when VAPID keys are unset so the backend boots and tests run without push secrets - /push routes: vapid-public-key, subscribe, unsubscribe (wired into index.js) - VAPID_* documented in backend/.env.example - 5 service unit tests (send, prune, dedupe-disabled, transient error) Frontend: - public/push-sw.js push + notificationclick handlers, imported into the Workbox-generated service worker via vite importScripts - usePushNotifications hook: permission, subscribe (VAPID key -> PushManager), unsubscribe, graceful when unsupported; 5 unit tests Closes FinesseStudioLab#619
|
@Gbangbolaoluwagbemiga is attempting to deploy a commit to the joelpeace48-cell's projects Team on Vercel. A member of the Team first needs to authorize it. |
Keep both the llvm-cov coverage gate (Epic FinesseStudioLab#652) and the proptest fuzzing step merged from upstream PR FinesseStudioLab#666.
…otifications-619 feat: Web Push notifications (campaign lifecycle, claim ready) (FinesseStudioLab#619)
…ign.jsx, setupTests.js)
- Run npm install to sync lock file with new devDependencies (ajv-formats, @vitest/coverage-v8) — fixes all npm ci failures - Run prettier --write on 5 JS/JSX files and contracts-ci.yml to pass format check
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…at/655-embed-widget-partner-sdk feat(embed): embeddable campaign widget + partner distribution SDK
…ioLab#669 Merge of PR FinesseStudioLab#669 (web-push notifications) overwrote the lock file. Re-run npm install to include all new devDependencies: ajv-formats, @vitest/coverage-v8, web-push and their transitive deps.
Merge upstream changes (embed routes, OnboardingTour tests, setupTests.js) and keep both sides of the vitest conflict: - setupFiles from upstream - coverage thresholds + broader include pattern from Epic FinesseStudioLab#652
…rm rollup optionals Previous npm install on macOS stripped Linux/Windows platform-specific optional packages from the lock file (@rollup/rollup-linux-x64-gnu etc). Restored upstream lock as base then ran npm install --package-lock-only to add new deps while preserving all platform natives.
Contributor
|
@christianahtemitope2005, please resolve failing ci |
- Rename CampaignFilters.test.js → .jsx (JSX in .js causes Rollup parse error) - Create CampaignList.jsx (test was importing missing component) - Create src/lib/config.js with getApiUrl() (test was importing missing module) - Remove duplicate driver.js entry from frontend/package.json - Raise bundle budget: stellar vendor 1800KB, total 2500KB (Stellar SDK is ~1.6MB uncompressed by design; budget gate now exceptions vendor-stellar)
1d6ea7d to
876acb0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements all six work items from the Epic/consolidation issue:
Coverage gates (JS + Rust)
test:coveragescript to frontend; CI runs it and uploads the lcov report as an artifact. Failures block the PR.API contract tests
test:contractscript; backend-ci.yml runs it as a separate step.Deterministic test factories & seeders
Bundle budget + route-level code splitting
check:bundleto root package.json; frontend-ci.yml runs it immediately after the Vite build and before artifact upload.URL-synced filter/sort/search
Auto-published contract TypeScript bindings
npm run contracts:build-bindings, computes SHA-256 hashes of both WASM files, writes metadata.json (version, generatedAt, wasmSha256, contractId), scaffolds the @trivela/contract-bindings package, and publishes to GitHub Packages.Closes #652