Better Dispute — Product Spec (Spec Kit)
1) Document Control
- Product: Better Dispute
- Version: v0.2 (draft)
- Status: Proposed for MVP
- Scope: Single-file browser app (
HTML + CSS + JS) with production-grade behavior - Architecture: Strict MVC (controller-authoritative)
- Storage: GitHub users for identity, GitHub Issues as append-only event store
2) Problem Statement
Most online disagreement is unstructured and spirals into noise. Better Dispute structures disagreement into explicit, auditable, turn-based exchanges between people so claims, questions, and answers can be tracked and resolved.
3) Objectives
- Deliver a production-quality MVP in one HTML file, no frameworks.
- Preserve immutable history through append-only issue events.
- Enforce all business rules in controller methods.
- Drive all UI state and controls from controller capability checks.
- Make every post shareable by canonical URL.
- Support duel-style dispute flow, including objections and crickets.
4) Non-Goals (MVP)
- Multi-file frontend architecture.
- Real-time sockets/push infrastructure.
- Rich media beyond one optional image per post.
- Full moderation platform (keep minimal guardrails only).
- Private/distributed tenancy beyond one public data repo.
5) Product Principles
- Controller is source of truth.
- View is dumb renderer.
- All writes are append-only events.
- Turn-state must always be obvious.
- URL must reconstruct app context.
6) Core Domain
6.1 Person
- GitHub-backed user identity.
- Fields:
id,githubId,handle(e.g.,@name),isSystem. @strawmanis a reserved system person.
6.2 Post
- Types:
Assertion,Challenge,Answer. - Every post belongs to a rooted tree where root is an
Assertion. - Top-level assertion content constraint: text XOR image (exactly one).
- Non-root posts may include text and optional one image.
Challengesubtypes:Interrogatory(Yes/No)Objection(form/validity/relevance/leading/etc.)
AnswerforInterrogatoryrequiresyesNoplus optional text.
6.3 Dispute
- First-class object created by a challenge between two people.
- Unique
id, participants, root assertion link, turn state, status. - Counter-challenge supports turn-based duel continuation.
- Challenges inside disputes may spawn nested disputes.
6.4 CricketsCondition
- Negotiated timeout model for failure-to-answer outcomes.
- States:
proposed,agreed,disputed,rejected,expired.
7) Controller Contract (Authoritative)
Controller methods (minimum):
canChallenge(person, post)canAnswer(person, challenge)canDispute(person, post)canAgree(person, assertion)canProposeCrickets(person, dispute)canResolve(person, dispute)
Controller-enforced rules:
- No person may challenge their own post.
- A person may challenge a given post at most once (lifetime in MVP).
- Only the targeted person may answer a challenge.
- Controls are disabled if disallowed (not hidden).
- Latest actionable post is highlighted.
- Successful submit always refreshes state from source of truth.
8) View Contract (Dumb Rendering)
- View reads controller state only.
- View renders enabled/disabled controls from controller booleans.
- View never decides permissions.
- View never mutates model directly.
9) UX Specification
9.1 Global Shell
- Dark theme with selective colorful accents.
- Header: balances icon (home), “Better Dispute”, version (right).
9.2 Home View
- New top-level assertion composer:
- text input
- image input (URL for MVP)
- post-as-
@strawmanoption
- Summary cards of top-level assertions/disputes.
- Cards are clickable like X-style thread cards.
- “Your turn” badge where actor has pending move.
- Terminal cards (no challenges) shown subtly as non-actionable.
9.3 Dispute View
- Back button pops one level at a time to Home.
- Parent chain shown as arrows (no “lineage” label).
- Flattened duel projection of relevant subtree.
- Post icons:
!assertion,?challenge,✓answer. - Visual depth via slight card stacking.
- Challenge action opens inline slide-up composer.
- Answer composer:
- required yes/no for interrogatory
- optional text
- optional counter-challenge field
- First counter-challenge switches layout to two lanes:
- original lane left
- counter lane right
- chronologically interleaved entries
- Show “Your turn” indicator.
- Disable unavailable actions and highlight latest actionable post.
9.4 Notifications
- MVP in-app notifications:
- “You were challenged”
- “Your answer was challenged”
- Home-level awaiting-move badges.
10) URL & Routing Requirements
Canonical query params (MVP):
view=home|disputeroot=<rootAssertionId>dispute=<disputeId>post=<postId>
Requirements:
- URL fully reconstructs current context.
- Every post has copy-canonical-URL action.
- Deep links work for internal/external sharing.
11) GitHub Storage Model (Append-Only)
- One dedicated public GitHub repository stores app data.
- Every event is a new GitHub issue (never mutate historical events for state transitions).
- Issue body contains canonical JSON payload + readable content.
- Labels index entity type and relational keys (root/dispute/parent/participants).
- Canonical ordering: issue number ascending, tie-break by event id.
- Invalid events are flagged by derived state, not deleted.
12) Security, Safety, and Reliability
- Strict payload schema validation before writes.
- Escape/sanitize rendered content to prevent XSS.
- Constrain image sources and file characteristics.
- Keep auth token in memory only for MVP.
- Handle GitHub API errors, retries, and eventual consistency.
- Throttle write actions client-side to reduce abuse/rate-limit pressure.
13) Clarifying Questions (to improve final product spec)
- Should there be one global dispute repo forever, or future multi-tenant repos?
- Is PAT-based in-browser auth acceptable for MVP, or do you want OAuth broker now?
- Should observers be allowed to challenge directly, or must they “agree” first?
- Do objections require categories, or free-form objection text only?
- What exact visual rule should define a “terminal” non-clickable summary card?
- Should challenge uniqueness reset after dispute resolution, or stay lifetime-unique?
- What is the default crickets duration if no agreement is reached?
- Should crickets countdown pause when GitHub API is unavailable?
- Are “resolution offers” a dedicated post subtype or assertions with a resolution flag?
- What minimum abuse controls are required at launch (report, mute, blocklist)?
14) Integrated MVP Decisions (assumptions applied now)
To unblock implementation, this draft integrates the following:
- Data tenancy: single dedicated public repo.
- Auth: in-session token for authenticated writes.
- Read mode: anonymous read-only allowed.
- Challenge uniqueness: lifetime one-per-person-per-target-post.
- Join rule: must agree with root assertion before answering linked challenges.
- Terminal card rule: root has no challenges.
- Crickets default: 24 hours when no agreed condition exists.
- Objection model: free-form text for MVP.
- Image input: URL-based only in MVP (upload flow deferred).
- Notifications: in-app only (no push/email).
15) Acceptance Criteria (MVP)
- User can create root assertion with text XOR image.
- User cannot challenge own posts.
- Duplicate challenge by same person against same post is blocked.
- Interrogatory answer requires Yes/No.
- Counter-challenge can be submitted during answer flow.
- First counter-challenge flips dispute view to two-lane layout.
- “Your turn” indicators are correct on Home and Dispute views.
- Disallowed actions are rendered disabled, never silently hidden.
- Every post has working canonical URL copy action.
- Deep links reconstruct view/dispute/post focus after refresh.
- App runs from one HTML file with no external JS/CSS frameworks.
- Derived state remains deterministic after reload.
16) Implementation Plan (Draft)
Phase 0 — Foundation & Contracts
- Define JSON schemas for
Person,Post,Dispute,CricketsCondition, and event envelope. - Define deterministic serializer/parser and validator utilities.
- Define controller action surface and state shape contract.
Phase 1 — GitHub Data Integration
- Implement API wrapper for GitHub Issues list/search/create with pagination and retry.
- Implement in-memory auth session handling and token input UX.
- Implement event query layer for root assertions, disputes, and post chains.
Phase 2 — Model Reconstruction & Rule Engine
- Reconstruct canonical model from append-only issue events.
- Implement authoritative controller checks (
canChallenge,canAnswer, etc.). - Enforce all action validations before any write.
Phase 3 — Home View
- Build app shell/header/version display.
- Build new assertion composer with strawman option and image URL.
- Render summary cards with turn badges and terminal-state visuals.
Phase 4 — Dispute View
- Render parent chain, timeline cards, and post-type icons.
- Implement challenge and answer inline slide-up composers with draft persistence.
- Implement turn indicators, disabled actions, and latest-actionable highlighting.
- Implement automatic single-lane to two-lane transition on first counter-challenge.
Phase 5 — Routing & Shareability
- Implement query-param router and deep-link hydration.
- Implement canonical URL builder for root/dispute/post scope.
- Implement copy-link UX on every post.
Phase 6 — Crickets & Resolution Flow
- Implement proposing/disputing/agreeing crickets conditions.
- Implement countdown expiration and prominent crickets event rendering.
- Implement resolution-offer assertions and resolution transitions.
Phase 7 — Hardening
- Security pass: sanitization, strict validation, token handling.
- Reliability pass: retries, stale-state refresh, conflict handling.
- UX/accessibility pass: keyboard, focus, contrast, concise status cues.
- Performance pass for large trees and summary-card lists.
17) Risks & Mitigations
- GitHub API rate limits → caching, debouncing, manual refresh controls.
- Eventual consistency → optimistic UI plus confirm-and-refresh cycle.
- Client-only auth risk → memory-only token and clear MVP warning.
- Spam/abuse → validation, local throttling, minimal moderation hooks.
18) Post-MVP Evolution
- Move from single HTML file to modular architecture.
- Add OAuth/GitHub App broker for secure auth.
- Add stronger moderation, reputation, and policy controls.
- Support richer media and upload workflows.