feat(disputes): dispute resolution with member voting and admin resolution - #250
Open
jajafwangshak86-ops wants to merge 1 commit into
Open
Conversation
Implements issue JointSave-org#208. Database: - disputes table (type, target, evidence URLs, status lifecycle open -> voting -> resolved_upheld/resolved_dismissed/expired, resolution notes, vote counters) and dispute_votes table with one vote per wallet per dispute; RLS public-read + realtime publication. - Disputes auto-expire 72h after filing. API: - POST/GET /api/disputes — members file disputes (one active per filer per pool, description <=2000 chars, up to 3 validated evidence links) - POST /api/disputes/[id]/vote — member-only voting; filer and target excluded; auto-resolves when one side reaches half the pool (rounded up) and logs to pool_activity - POST /api/disputes/[id]/resolve — pool admin closes any active dispute with a required written resolution note - POST /api/disputes/expire — CRON_SECRET-gared expiry job registered in vercel.json Frontend: - useDisputes hook: feed loading + Supabase Realtime subscription + optimistic voting that reconciles with server truth - DisputesPanel / DisputeCard / FileDisputeDialog components wired into the group page below the main grid; status badges, live countdown, quorum-style vote counter, admin resolve dialog - lib/disputes.ts pure helpers with 8 node unit tests; en/es i18n
Sendi0011
self-requested a review
August 25, 2026 07:55
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.
Closes #208
Summary
Adds a complete dispute resolution flow for pools: members can file disputes (against another member or an admin action), the pool votes to uphold or dismiss, and the admin can force-resolve with a written note.
Database (
20260824010000_dispute_resolution.sql)disputes: filer, optional target member, type (missed_deposit/unfair_penalty/admin_abuse/member_misconduct/other), description (≤2000 chars), up to 3 evidence URLs, status lifecycleopen → voting → resolved_upheld / resolved_dismissed / expired, vote counters, resolution note + resolver + timestampdispute_votes: one vote per wallet per dispute (composite PK)API
POST /api/disputesGET /api/disputes?pool_id=POST /api/disputes/[id]/votePOST /api/disputes/[id]/resolvePOST /api/disputes/expireCRON_SECRET-gared job that flips stale disputes to expired — registered invercel.jsonAll state changes are mirrored into
pool_activity(dispute_filed,dispute_resolved).Frontend
DisputesPanelrendered full-width on the group page: status badges, live voting countdown, vote counter showing threshold, resolution notesFileDisputeDialog: type select, optional target member picker, live character counter, repeatable evidence URL inputsuseDisputes: Supabase Realtime subscription keeps the feed live without refresh; optimistic vote updates reconcile against server truthlib/disputes.tspure helpers covered by 8 node unit tests (registered intest:unit); full en/es translationsVerification (all green locally)
pnpm lint,pnpm format:check,test:unit(210 pass incl. new disputes tests),test:components(101),pnpm build, bundle budget checktsc --noEmit)