Skip to content

Trust & Reputation System — Specification

Status: Draft v2 Date: 2026-04-04 Related: rating-system-summary.mdSupersedes: v1 (thumbs up/down peer reviews)


Design Principles

  1. Praise is public, penalties are private — 당근 매너온도 model. No negative UI. Bad behavior = silent demotion.
  2. Trust tiers unlock privileges — VRChat/Stack Overflow model. Reputation is functional, not vanity.
  3. Structural verification > honor system — Mutual confirmation of attendance + scores.
  4. 총무 authority preserved — System informs, never auto-expels.
  5. Club opt-in — Attendance tracking OFF by default. Each club decides.

Part 1: Trust Tiers (신뢰 등급)

4 Tiers — Social Proof, Not Access Control

Trust tiers are visible badges that signal reliability. They do NOT gate any core feature. See platform-architecture.md for the full feature access model.

TierBadgeHow EarnedWhat It Signals
🌱 새싹(no badge)Account createdNew user — no track record yet
🎾 활동Small badge5+ sessions confirmed"This person actually plays"
신뢰Prominent badge20+ sessions, 90%+ att (if tracked), 10+ manner tags"Reliable, well-liked"
🏅 베테랑Gold badge50+ sessions, 6+ months, 20+ tags"Club pillar"

What tiers do: Profile badge, optional recruitment filter, slight discovery boost. What tiers do NOT do: Block joining clubs, block RSVP, block any core feature.

Progression Algorithm (Hidden — VRChat Model)

Players do NOT see exact progress toward next tier. They see their current tier and a qualitative hint:

🎾 활동 멤버
"신뢰 등급까지 조금 더!"    ← vague encouragement, no exact number

Why hidden: Prevents gaming (e.g., "I need 3 more confirmations to unlock 신뢰"). Players focus on playing tennis, not optimizing metrics.

Inputs to Tier Calculation

SignalWeightSource
Sessions attended (confirmed)HighMutual confirmation
Attendance rate (if club tracks)MediumAttendance system
Manner tags receivedMediumPost-session praise
Account ageLowRegistration date
Matches with score confirmed by both sidesMediumScore verification
총무 endorsementHighManual boost by admin
No-show strikesNegativeAttendance system
Reported by other membersNegativeReport system (hidden)

Demotion

  • Silent — no notification for demotion itself
  • Triggered by: 3+ no-shows in rolling window, reported by 3+ unique members, 총무 manual action
  • Player sees privileges disappear, not a "you've been demoted" message
  • 총무 sees demotion reason in dashboard

Part 2: Manner Tags (매너 태그) — Praise-Only System

The 당근 Insight

당근마켓's 매너온도 only has positive review options. There is no "bad manner" button. Negative signals come from behavioral data (reports, no-shows, disputes) — never from a UI that lets users rate others negatively.

Why this works: In small communities (10-30 person clubs), a visible 👎 system creates hostility and retaliation. Praise-only keeps the social dynamic positive while bad actors are handled through structural mechanisms.

Post-Session Manner Tags

After a session, players can give manner tags to anyone they played with:

TagKoreanWhat It Signals
🤝매너가 좋아요Good sportsmanship, respectful
시간 약속을 잘 지켜요Shows up on time
🎯실력이 정확해요Skill matches their level
💬소통이 잘 돼요Good communication, friendly
🔥열정적이에요Enthusiastic, brings energy

How It Works

Session ends
  → 2h later: push notification "오늘 함께한 멤버에게 매너 태그를 남�� 보세요"
  → Tap → lightweight bottom sheet:
      [Player 1]  🤝 ⏰ 🎯 💬 🔥  (tap to toggle, multi-select)
      [Player 2]  🤝 ⏰ 🎯 💬 🔥
      ...
  → Submit → done (under 10 seconds)
  → Tags accumulate immediately (no dual-blind needed — it's all positive)

Display on Profile

🤝 매너가 좋아요  ×24
⏰ 시간 약속을 잘 지켜요  ×18
🎯 실력이 정확해요  ×15

Shown to: the player themselves + club members + 총무. Counts only, not who tagged.

No Negative Feedback UI

If a player has bad manners, the correct flow is:

  1. Report (신고) — goes to 총무, not public
  2. 총무 investigates, warns the member privately
  3. Repeated reports → silent trust tier demotion
  4. Extreme cases → 총무 removes from club (existing club management feature)

Part 3: Attendance Score (unchanged from v1)

Club Opt-In

SettingDefaultDescription
출석 관리OFFEnable attendance tracking
취소 페널티OFFEnable strike system (requires 출석 관리)
무료 취소 기한24hFree cancellation window (12h / 24h / 48h)

Key Rule

  • Not RSVPing = no impact. People have lives.
  • RSVPing "참가" then not showing = the problem this solves.

Cancellation Tiers

TimingClassificationStrike Value
Before free windowFree cancel0
Within free windowLate cancel0.5
< 12h or no-showNo-show1.0
총무 excusedExcused0

Consequences (Private, Graduated)

Strikes (rolling 20 sessions)Action
1Gentle nudge notification
2Warning + 1-session RSVP cooldown
3+Flagged to 총무 + trust tier demotion signal

Visibility

  • Member: Own rate + streak (private)
  • 총무: All members' rates + flagged list
  • Other members: Nothing

Part 4: Mutual Confirmation (Structural Verification)

The Core Mechanism

Instead of trusting self-reports, require both sides to confirm key facts:

WhatWho ConfirmsIf Only One SideIf Both Confirm
AttendancePlayer + 총무 (or auto-checkin)Counts, but lower confidenceFull confidence
Match scoreBoth teams (existing score verification)Counts with existing K-factor1.1× confidence bonus to ELO delta
Session happened총무 marks session completeRequired to trigger reviews

Auto Check-in (Optional)

If the club's court has a known location, players who are physically present (GPS within 200m at session time) get auto-confirmed attendance. No manual check-in needed.

Privacy: Location check is a one-time ping at session start, not continuous tracking. Opt-in per player.


Part 5: Recruitment Filters (호스트 필터)

When a 총무 or host creates a session or recruiting post, they can set minimum requirements:

FilterOptionsUse Case
실력 (ELO tier)Minimum tier (e.g., Gold III+)Skill-appropriate matching
신뢰 등급Minimum trust tier (e.g., 활동+)Filter out brand-new accounts
참석률Minimum % (e.g., 80%+)Ensure reliable attendance for paid courts
성별남성 / 여성 / 전체Gender-specific sessions

How Filters Appear to Applicants

[Session Card]
오픈 복식 경기  ·  4/12 토 14:00
🎾 Gold III 이상  ·  ⭐ 신뢰 등급 이상  ·  참석률 85%+

If a player doesn't meet the filter, the RSVP button shows: "참가 조건을 확인해 주세요" with the specific requirement they're missing.

Filter Visibility

  • Filters are shown on the session card — transparent, not hidden
  • Players know exactly why they can't join
  • This incentivizes building trust tier + maintaining attendance — because it unlocks access

Part 6: The Integrated Player Profile

Self-View (내 프로필)

[Avatar]  김민수
Gold II · 1,420 (±65)
신인부 ~ 오픈부  ·  NTRP 3.0-3.5

⭐ 신뢰 멤버                          ← trust tier badge

🤝 ×24  ⏰ ×18  🎯 ×15  💬 ×12       ← manner tag counts

참석률 94% · 연속 7회                  ← private, only I see this

Club Member View

[Avatar]  김민수
Gold II · 1,420
⭐ 신뢰 멤버

🤝 ×24  ⏰ ×18  🎯 ×15               ← manner tags (public praise)

No attendance rate, no negative scores. Just: tier + trust badge + positive tags.

총무 View

[Avatar]  김민수
Gold II · 1,420
⭐ 신뢰 멤버

🤝 ×24  ⏰ ×18  🎯 ×15  💬 ×12  🔥 ×8
참석률 94% · 노쇼 1회/20회
신고: 0건

Discovery / Recruitment View

[Avatar]  김민수
Gold II
⭐ 신뢰 멤버
🤝 ×24

Minimal — tier + trust badge + top manner tag count.


Part 7: Data Model

trust_tiers (enum, not a table)

'sprout' | 'active' | 'trusted' | 'veteran'

profiles (new columns)

sql
ALTER TABLE profiles ADD COLUMN trust_tier TEXT DEFAULT 'sprout'
  CHECK (trust_tier IN ('sprout', 'active', 'trusted', 'veteran'));
ALTER TABLE profiles ADD COLUMN trust_tier_updated_at TIMESTAMPTZ;
ALTER TABLE profiles ADD COLUMN total_sessions_confirmed INTEGER DEFAULT 0;
ALTER TABLE profiles ADD COLUMN global_attendance_rate REAL;

manner_tags

sql
CREATE TABLE public.manner_tags (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  giver_id UUID NOT NULL REFERENCES profiles(id),
  receiver_id UUID NOT NULL REFERENCES profiles(id),
  session_id UUID NOT NULL REFERENCES sessions(id),
  tag TEXT NOT NULL CHECK (tag IN ('manner', 'punctual', 'accurate', 'communicative', 'enthusiastic')),
  created_at TIMESTAMPTZ DEFAULT now(),
  UNIQUE (giver_id, receiver_id, session_id, tag)
);

attendance_records

sql
CREATE TABLE public.attendance_records (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES profiles(id),
  session_id UUID NOT NULL REFERENCES sessions(id),
  club_id UUID NOT NULL REFERENCES clubs(id),
  rsvp_status TEXT NOT NULL,
  final_status TEXT NOT NULL
    CHECK (final_status IN ('attended', 'no_show', 'late_cancel', 'free_cancel', 'excused')),
  strike_value REAL DEFAULT 0,
  confirmed_by TEXT DEFAULT 'self'
    CHECK (confirmed_by IN ('self', 'mutual', 'gps', 'admin')),
  created_at TIMESTAMPTZ DEFAULT now(),
  UNIQUE (user_id, session_id)
);

clubs (new columns)

sql
ALTER TABLE clubs ADD COLUMN attendance_tracking_enabled BOOLEAN DEFAULT false;
ALTER TABLE clubs ADD COLUMN attendance_cancel_window_hours INTEGER DEFAULT 24;

member_reports (for silent demotion signals)

sql
CREATE TABLE public.member_reports (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  reporter_id UUID NOT NULL REFERENCES profiles(id),
  reported_id UUID NOT NULL REFERENCES profiles(id),
  club_id UUID NOT NULL REFERENCES clubs(id),
  session_id UUID REFERENCES sessions(id),
  reason TEXT NOT NULL,
  resolved BOOLEAN DEFAULT false,
  resolved_by UUID REFERENCES profiles(id),
  created_at TIMESTAMPTZ DEFAULT now()
);

Part 8: Edge Cases

ScenarioHandling
Player not RSVPing regularlyNo impact. System only measures RSVP→no-show.
Regular personal absencesNo impact as long as they don't RSVP for sessions they can't attend.
Club doesn't want attendance trackingOFF by default. No data collected.
New player with 0 manner tagsShows trust tier (새싹) only. No empty "0 tags" display.
Player in multiple clubsTrust tier is global. Manner tags aggregate across clubs. Attendance per club.
총무 gives manner tagsCounts the same as any member. No special weight in UI.
Someone gives all 5 tags to everyone every sessionTags from the same giver to the same receiver cap at 1 per tag per session (UNIQUE constraint). Outlier givers don't skew because counts across many givers.
GPS auto-checkin at wrong locationOnly triggers within 200m of registered court. Player can manually confirm if GPS fails.
Player gets reported but it's a personal dispute총무 reviews. Can dismiss report. Dismissed reports don't count toward demotion.

Part 9: How This Replaces the Honor System

ProblemOld (Honor System)New (Structural)
Skill sandbaggingTrust self-assessmentELO converges via K=40/48 + strike system
No-showsSocial pressureAttendance tracking + RSVP cooldown + deposit (future)
Bad manners총�� hears complaintsReport → 총무 dashboard → silent demotion
Score disputesOne side enters scoreMutual confirmation (both teams verify)
Fake accountsNoneTrust tiers gate privileges (새싹 can't host or recruit)
Club hopping to avoid reputationNoneGlobal trust tier + manner tags follow across clubs

The key insight: every enforcement mechanism is structural (built into the system's rules) rather than social (dependent on people being honest).


References

  • 당근마켓 매너온도: praise-only public feedback, hidden penalty algorithm
  • Airbnb Superhost: automated badge with hard metric thresholds, silent loss
  • Playtomic: reliability factor, coach-verified ratings
  • VRChat: hidden progression, privilege-gated trust tiers
  • Stack Overflow: reputation unlocks privileges, not vanity
  • Urban Sports Club / Playtomic: tiered cancellation penalties
  • Korean 동호회 bylaws: 총무 authority, 2/3 vote expulsion

Markdown remains the source of truth. Run yarn docs:check before handoff.