Skip to content

Audit 2 — the data contract per page (2026-11-01) ​

Status: Reference · Date: 2026-11-01 · The table behind owner-review-52-audits-2026-11-01.md § 2. Read-only, at HEAD 191e628bd.

Source: board spec rows (Reads, Taps, Stack, Rulings) of each page section in docs/twomore_design_system/wireframes/*.jsx, joined to page-levels.json and page-code-map.md; the data layer was checked by grep over ports, hooks, supabase/migrations (latest definition), generated.types.ts Tables and supabase/functions. Nothing was run against a database; only the repo at read time.

Summary ​

  • Pages: 82. covered 54 · partial 23 · missing 5.
  • covered: L0-02, L0-05, L1-03, L1-03c, L1-04, L2-03, L3-03, L3-05, L3-06, L3-07, L3-09, L3-13, L3-14, L3-15, L3-16, L3-18, L3-19, L3-20, L3-21, L3-22, L3-23, L3-24, L3-27, L3-28, L3-29, L3-30, L3-31, L3-33, L3-34, L3-37, L3-41, L3-42, L3-43, L3-44, L3-45, L4-01, L4-02, L4-03, L4-04, L4-05, L4-06, L4-07, L4-08, L4-09, L4-10, L4-11, L4-12, L4-13, L4-21, L4-15, L4-17, L4-18, L4-19, L4-20
  • partial: L0-01, L0-04, L0-06, L0-07, L1-01, L1-02, L1-05, L2-01, L2-02, L2-04, L2-06, L3-01, L3-04, L3-10, L3-11, L3-17, L3-25, L3-32, L3-35, L3-36, L3-38, L3-39, L3-47
  • missing: L0-03, L3-08, L4-14, L4-16, L3-46
  • Method note: board hook names are not always the shipped names (useConsents/useUserConsents, useAddThreadMembers/useAddThreadParticipants, useSessionRules, deriveMatchResultState/deriveMatchResult, useClubByInviteCode); such mismatches are noted under "unsure" and are not counted as gaps when an equivalent exists. Board "data owed" lines that the repo already satisfies (player_matchups_by_format for 선수별 전적 and 기록) are marked as such.

Distinct gaps by kind ​

New table ​

  • identity verification store (CI hash unique, real name, birth date, phone, foreigner flag, verified_at); profiles has only phone, birth_year, nationality_code. Pages: L0-04
  • per-user hidden 소식 item ids (숨기기/되돌리기) and per-club 소식 mute (이 클럽 소식 끄기): nothing stores them (signal_preferences is per signals category). Pages: L1-01
  • club_dues_plans (name, amount, cadence incl. one_time/every_n_months/per_session, due rule, grace, proration, applies-to role/조); dues has only month/year, no plan_id. Pages: L2-01, L3-11, L4-14
  • attendance excuse request (one per attendance_record, reason <=200 chars, answered by host/총무, 거절됨 state): only excuse_attendance_record(p_record_id), an official one-way mark, exists. Pages: L3-01
  • match_notes (match_id, author_id, mood good|ok|bad, body <=500; own-row RLS): no table, port or hook exists (practice_logs dropped in 00702). Pages: L3-08, L3-38
  • club ledger (opening balance, dated income/expense entries with 분류 + 영수증; a confirmed 회비 = one income entry) with an aggregate read (balance, month spend) and a CSV export: no table, port or hook. Pages: L3-11, L3-46
  • club join-vote store for the 멤버 투표 admission rule (votes, deadline, objection) with a cast-vote RPC and a read hook (club_join_request has no vote data). Pages: L3-32

New column ​

  • rsvps.leaves_at, available_until_round, checked_in_at, checked_out_at (rsvps has arrives_at and available_from_round only; no migration adds them). Pages: L2-02, L3-01, L3-36
  • profiles 주 종목 and 선호 시간 (no column on profiles or user_preferences; no editor page among the 82 sets them). Pages: L2-06
  • dues.plan_id, dues.period_start, dues.period_end (rows generated per period). Pages: L3-11, L4-14
  • reminder log for 마지막 알림 (who/when) on dues and on session_payments (no column or table). Pages: L3-11
  • matches.stage and group (tournament matches live in tournaments.state JSONB today), matches.scheduled_start (09:30 시작 queue). Pages: L3-38, L3-39
  • saved generation preset 소셜/실력별/고정/랜덤 on sessions (board: only the three score-driven generation_style_id values persist; GenerationPolicy is spine-only, no persisted axes). Pages: L3-47
  • persisted GenerationPolicy axes the page offers beyond recording (host-selected pool 선택한 사람만, 만드는 시점, 확정 시점): generation-policy.entity.ts derives an effective policy and persists no policy column. Pages: L3-47

New RPC / RPC extension ​

  • get_public_share_preview extension: session fee, approval mode, isPickup, venue district; match venue/format/day; profile NTRP + 신뢰 점수 (it returns totalSessionsConfirmed and the raw trust tier key the board forbids); club region as words. Pages: L0-06, L0-07
  • anonymous, minimal, rate-limited invite reader for the /join-club card (lookup_club_by_invite_code returns an id only; the whole clubs row must not leave). Pages: L0-07
  • trust record read (신뢰 점수 = 50 + 12-month points held 0..100, none before 5 recorded sessions; per-record points, rule applied, venue/day, streak, praise count): no trust_score column, RPC or domain function exists (profiles has trust_tier only). Pages: L1-05, L2-06, L3-01, L3-25
  • visibility-filtered reads of ANOTHER person (match history, achievements, play facts, last-20 attendance + praise for shared-context viewers); useMatchHistory and useAchievements read the viewer's own. Pages: L2-06, L3-04
  • attendance record query joined to venue + the cancel-time rule applied (board: "data owed: the venue joins the record query"). Pages: L3-01
  • send reminder for one member / all 미납 members (dues) and for a session fee (참석자 관리), logged by who/when: no port method or RPC (generate_dues_reminders was dropped in 00405). Pages: L3-11, L3-35
  • period (전체/이번 주/이번 달) narrowing on the leaderboard reads (get_club_rating_ranks, get_regional_ranking, get_public_profile_leaderboard, findTopByRating take none). Pages: L3-17
  • submit_match_score expected-revision parameter and the typed refusal score_changed (the RPC takes no revision; matches.score_revision exists). Pages: L3-38
  • move tournament matches into matches (stage/group) and the group-table/bracket/advance reads; retire tournaments.state JSONB + useTournament/useAdvanceTournament/useCancelTournament. Pages: L3-38, L3-39, L3-47

New hook / port method ​

  • signUp(email, password) on AuthServicePort (board: only if the OAuth route slips). Pages: L0-03
  • useHomeNewsFeed: the 소식 feed of 일정/결과/게시글 (+사진) across the viewer's clubs with a hide filter (HomeFeedScreen uses useHomeData, period recaps, nearby/discovery cards; no feed query or RPC exists). Pages: L1-01
  • bulk pending join requests for the 클럽 list (only per-club useMyJoinRequest / findMyJoinRequest(clubId) exist). Pages: L1-02
  • useTrustRecord(userId[, clubId]) over the trust read (existing: useMemberStrikeRecords, useAttendanceSummary, useExcuseAttendance). Pages: L1-05, L3-01
  • arrival/exit stamp mutation (도착했어요, 먼저 갈게요) writing rsvps.checked_in_at/checked_out_at (only check_in_attendance_gps RPC from 00318 and useArrivalAvailability exist). Pages: L2-02, L3-36
  • a venue's public 매치 (in play + planned), clubs whose home_court_venue_id is the venue, and the owner's waiting-suggestion count (useOpenPickups has no venue filter; no hook lists them). Pages: L2-04
  • useMatchNotes(userId, 50) and a save/upsert mutation for a 경기 노트 (no hook, no port). Pages: L3-08, L3-38
  • useDeclineChallenge (InterClubChallengeRepositoryPort.decline exists; only useAcceptChallenge is a hook). Pages: L3-10
  • 정산 나누기 cost-split write for a session with no fixed fee (sessions.cost_total exists; no hook confirmed). Pages: L3-35
  • getUserIdentities / linkIdentity / unlinkIdentity on AuthServicePort + hooks (port has signInWithOAuth, signInWithEmail, signOut, changePassword only). Pages: L4-16

New edge function ​

  • verify-portone-identity edge function (PortOne v2 identity check, age/CI/duplicate; only verify-portone-payment and portone-webhook exist). Pages: L0-04
  • AI 요약 job/edge function for 테니스 여정 (supabase/functions has none), plus an entitlement key for the player plan gate (getEffectivePlan/useFeatureGate exist; PLAN player exists). Pages: L3-08

Config ​

  • supabase/config.toml configures only [auth.external.apple]; Kakao, Naver (custom) and Google providers are not configured locally; enable_manual_linking = false (needed by 연결된 계정). Pages: L0-01, L4-16

Candidates the lead named ​

  • match note: no match_notes table, port or hook (verified: not in generated.types.ts Tables, no hook; migration 00702 drops practice_logs). Needed by L3-08 and L3-38.
  • 테니스 여정 AI 요약: no edge function (18 functions plus _shared, none AI), no storage; plan gate pieces exist (subscription.entity.ts PlanType free/player/basic/pro/club_plus, getEffectivePlan, useFeatureGate) but no AI entitlement key was found.
  • 공개 프로필 플레이 facts (주 종목, 선호 시간): profiles holds neither (columns listed in generated.types.ts); clubs has primary_day/primary_time_slot, profiles does not.
  • 대진 짜기 planned policy axes: generation-policy.entity.ts is spine-only (no persisted policy column); sessions.generation_style_id persists only the score-driven styles.
  • 참가비 내역: covered by useMySessionPayments (session_payments has refund_state, submitted_at). The route is new, the data is not.
  • 장부: no ledger table (see L3-46). 입금 계좌: club_bank_accounts + useClubBankAccount exist (L4-21 covered). 연결된 계정: no linkIdentity/getUserIdentities anywhere and enable_manual_linking = false. 신뢰 기록 meter: no trust score anywhere (profiles.trust_tier only). 행사: club_events + useCreateClubEvent/useClubEvents exist. Owed pages 정기 일정 / 조 / 앨범 관리: hooks exist (useClubRegularMeets, useClubGroups, useManageClubMedia), no page code.

Per page ​

Columns: Page | Reads | Writes | Provided by | Gaps | Status. where the board spec is silent or a name did not resolve, the Gaps cell ends with "(unsure: ...)".

PageReadsWritesProvided byGapsStatus
L0-01 시작signed-out session; provider list (Kakao, Naver, Google, Apple)signInWithOAuth(provider); signInWithEmailAuthServicePort.signInWithOAuth/signInWithEmail; useAuth; getAuthErrorMessage[config] supabase/config.toml configures only [auth.external.apple]; Kakao, Naver (custom) and Google providers are not configured locally; enable_manual_linking = false (needed by 연결된 계정) (unsure: Hosted Supabase providers not checked; only supabase/config.toml read)partial
L0-02 이메일 로그인email + passwordsignInWithEmailAuthServicePort.signInWithEmail; useAuth().signInWithEmail; getAuthErrorMessagenonecovered
L0-03 이메일 가입emailclient signUp + email confirmation; resend mail; password resetuseAuth (sign-in only)[hook] signUp(email, password) on AuthServicePort (board: only if the OAuth route slips) (unsure: Board marks this flow "only if the OAuth route slips"; profile row is created at first sign-in)missing
L0-04 온보딩required consents for KR (getRequiredConsentsForJurisdiction, showAtSignup); pending identityVerificationIdPortOne identity verification result (ok/under_14/duplicate/failed); useUpdateProfile; useGrantConsent per buildConsentGrants; completeOnboardinguseUpdateProfile; useGrantConsent; consent rules (areRequiredConsentsGranted, buildConsentGrants); resolveOnboardingDestination[edge] verify-portone-identity edge function (PortOne v2 identity check, age/CI/duplicate; only verify-portone-payment and portone-webhook exist); [table] identity verification store (CI hash unique, real name, birth date, phone, foreigner flag, verified_at); profiles has only phone, birth_year, nationality_code (unsure: completeOnboarding/handleFinish names did not resolve as exports; step 2 and 3 appear to be existing screen code)partial
L0-05 업데이트가 필요해요runtime version vs app version policy; store URLRPC get_app_version_policy; useAppVersionGate; app-version-policy.service.port.tsnone (unsure: Store URL is empty until the stores list the app (EXT-8), external to the data layer)covered
L0-06 공유 링크club, venue, date, times, format, tier band, deadline, counts (no names); fee, approval mode, pickup flag, venue district; match venue/format/day/score tagapply (via 매치 미리보기)RPC get_public_share_preview(kind,id); usePublicSharePreview; public-share-preview.repository.port; useSessionQuery[rpc] get_public_share_preview extension: session fee, approval mode, isPickup, venue district; match venue/format/day; profile NTRP + 신뢰 점수 (it returns totalSessionsConfirmed and the raw trust tier key the board forbids); club region as wordspartial
L0-07 웹 공유 페이지same preview fields for /s /m /p /lb; invite card (/join-club): name, mark, region, level band, member count, join method, monthly duesRPC get_public_share_preview; RPC lookup_club_by_invite_code (id only)[rpc] get_public_share_preview extension: session fee, approval mode, isPickup, venue district; match venue/format/day; profile NTRP + 신뢰 점수 (it returns totalSessionsConfirmed and the raw trust tier key the board forbids); club region as words; [rpc] anonymous, minimal, rate-limited invite reader for the /join-club card (lookup_club_by_invite_code returns an id only; the whole clubs row must not leave) (unsure: generateMetadata, opengraph-image, robots noindex and og dimensions are web-app work, not data-layer; not counted as gaps)partial
L1-01 홈next joined game + state (cancelled/check-in/fee due/waitlist/confirmed); match in play and result; 할 일 (HomeTask kind,count,deadline); 소식 feed (일정, 결과, 게시글, 사진); hidden item ids; bell unreadhide / unhide a 소식 item; mute a club's 소식get_home_snapshot RPC + HomeSnapshotRepositoryPort; useHomeData; useHomeTasks / deriveHomeTasks; useNotificationBellState / useUnreadSignalCount; useMySessionPayment + sessionPaymentGateState[hook] useHomeNewsFeed: the 소식 feed of 일정/결과/게시글 (+사진) across the viewer's clubs with a hide filter (HomeFeedScreen uses useHomeData, period recaps, nearby/discovery cards; no feed query or RPC exists); [table] per-user hidden 소식 item ids (숨기기/되돌리기) and per-club 소식 mute (이 클럽 소식 끄기): nothing stores them (signal_preferences is per signals category)partial
L1-02 클럽내 클럽 rows (buildClubCardModel + focus, member_composition, preferred_format, regular_meet_days, dues_amount/dues_cycle, join_policy, last_activity_at); guest + archived clubs; 승인 대기 requests; 클럽 찾기 with facetsnone on the page (FAB opens create/join)useMyClubs; useMyGuestClubs; useViewerDiscoverClubs; RPC get_club_discovery_summaries (has last_activity_at); clubs columns focus/member_composition/preferred_format/dues_amount/dues_cycle/join_policy; club_regular_meets / clubs.primary_day[hook] bulk pending join requests for the 클럽 list (only per-club useMyJoinRequest / findMyJoinRequest(clubId) exist) (unsure: last_activity_at is returned by get_club_discovery_summaries (discovery); not verified for the 내 클럽 payload)partial
L1-03 경기open pickups (filters: date, region, event, format, court type, NTRP, fee); 예정 sessions; saved sessions; friendsGoing, distanceKm, used courtssave toggleuseOpenPickups / useOpenPickupsForMe; applyPickupFilters; useMyParticipatingSessions; useSavedSessionIds + useSessionsByIds + useToggleSaveSession; useSessionCardModels (friendsGoing, distanceKm); sessions.used_courts (00499)none (unsure: where.usedCourts is a model mapping change over an existing column, not a data gap)covered
L1-03c 경기 · 테니스장venue directory with kind/region/facility facets; saved venues; distance when location granted; open-now, court countsave toggleuseVenueFinderData; useVenueDirectory / useVenueViewCounts; useSavedVenueIds + useToggleSaveVenue; RPC count_venues_by_viewnonecovered
L1-04 기록rating + 30-day history + next tier; club rank neighbours; 최고의 파트너 / 천적 (>=5 matches); activity grid per day; newest 5 matchesshare record cardusePlayerProfileData; useClubRatingRanks (RPC get_club_rating_ranks); usePlayerFormatRecords (player_matchups_by_format, player_partner_records via findMatchupsByFormat/findPartnerRecords); computePartnerStats; useMatchHistory; usePlayerStatsDetail; useShareStatCardnone (unsure: Board text still says player_matchups needs a format column; player_matchups_by_format (discipline) and findMatchupsByFormat already exist)covered
L1-05 프로필profile; trust record (last 20 counted records, streak, strikes, praise count, 신뢰 점수, hidden <5 sessions); friends preview + count; achievementsuseProfile; usePlayerProfileData; useAttendanceSummary; useMannerTagCounts (manner_tags); useFriendsSummary / useFriends; useAchievements; useDmUnreadCount[rpc] trust record read (신뢰 점수 = 50 + 12-month points held 0..100, none before 5 recorded sessions; per-record points, rule applied, venue/day, streak, praise count): no trust_score column, RPC or domain function exists (profiles has trust_tier only); [hook] useTrustRecord(userId[, clubId]) over the trust read (existing: useMemberStrikeRecords, useAttendanceSummary, useExcuseAttendance) (unsure: manner_tags findCountsByUser counts tags; "distinct people who shared a match" praise count not confirmed)partial
L2-01 클럽 홈club, role, unread count; album + cover; home court (clubs.home_court_venue_id); 홈 feed: sessions, posts, album uploads, closed records; chat rooms; 기록 pane: season, standings, member records, 최고의 파트너; members by role; 회비 pane: own dues rows, session_payments, bank account, 납부 내역; non-member public sessions + photos as countsjoin by policy (바로/신청/초대 코드); leave club; reactions; 입금했다고 알리기; copy account number; role/조/내보내기 (member Sheet)useClub; useClubDetailBundle (RPC get_club_detail_bundle); useClubRole / useClubContext; useUnreadPostCount; useClubMedia / useApprovedPublicClubMedia; useClubPosts; useClubSessions / useCompletedClubSessions; useClubEvents; useClubRatingRanks, season_records; useClubMembers; dm_threads(club_id) via useVisibleDmThreads; useClubBankAccount; dues port findByMember + attestDuesPayment; club.repository joinOpenClub/applyToClub/joinByInviteCode; usePartnerCandidates[table] club_dues_plans (name, amount, cadence incl. one_time/every_n_months/per_session, due rule, grace, proration, applies-to role/조); dues has only month/year, no plan_id (unsure: 회비 pane "by the club's plan" depends on club_dues_plans; today dues are month/year rows with clubs.dues_amount/dues_cycle/dues_day)partial
L2-02 일정 상세session detail bundle + model (hero, facts, NTRP, who); roster (confirmed + waitlist); first 3 matches; my payment gate + countdown; weather; capabilities; pending applications; my planned hours and arrival stampRSVP / cancel; arrival stamp (도착했어요); exit time (먼저 가기); planned hours (참석 시간); save / reportuseSessionDetailBundle; useSessionDetailModel + buildSessionInfoModel; useSessionMatchSets; useMySessionPayment + sessionPaymentGateState + useDeadlineCountdown; useSessionWeather; useSessionCapabilities; useLiveSessionData; useSessionApplications; useToggleSaveSession; rsvps.arrives_at[column] rsvps.leaves_at, available_until_round, checked_in_at, checked_out_at (rsvps has arrives_at and available_from_round only; no migration adds them); [hook] arrival/exit stamp mutation (도착했어요, 먼저 갈게요) writing rsvps.checked_in_at/checked_out_at (only check_in_attendance_gps RPC from 00318 and useArrivalAvailability exist)partial
L2-03 매치 미리보기session; venue; public attendance counts; my application; host profile; club (public club session)apply (참석 신청/바로 참석); withdraw application; DM to hostuseSessionQuery; useVenue; usePublicSessionAttendance; useMyApplicationForSession; useProfile; useClubRole; useClub; useApplyToSession; useUpdateApplicationStatus; useInitiateDmThreadnonecovered
L2-04 장소 상세venue facts + hero + map; source values / 확인됨; pending + my corrections; saved flag; distance; edit approver + owner scope; booking alert; public 매치 at the court (live + planned); clubs with this home court; owner's waiting suggestion countsave; booking alert switch; 코트 수 바꾸기; remove from club list; propose / claim (links)useVenue; useDirectoryVenue / useDirectoryVenueDetail / useVenueCourts; useVenueHeroImage; useVenueSourceValues; useVenuePendingCorrections / useMyVenueCorrections; useSavedVenueIds + useToggleSaveVenue; useUserLocation; useIsVenueEditApprover / useVenueOwnerScope; useBookingAlert; useVenueContributions({scope,venueId}); columns sessions.court_venue_id, clubs.home_court_venue_id, court_venues.venue_id[hook] a venue's public 매치 (in play + planned), clubs whose home_court_venue_id is the venue, and the owner's waiting-suggestion count (useOpenPickups has no venue filter; no hook lists them) (unsure: useVenueContributions with scope + venueId may already give the owner's suggestion count)partial
L2-06 공개 프로필identity, face (can_view_face), level, 구력, 신뢰 점수 (none before 5 sessions), rating (singles + doubles); friendship state; 총 경기/승률 + last five results; primary club(s); earned badges; 주 종목, 선호 시간; last-20 attendance + praise (shared-context only); 나와의 전적 (상대 + 같은 편)friend request / cancel / accept / decline; block / unfriend; report; DM requestusePublicProfile (profiles.findPublicView, can_view_face); useFriendship + friend mutations; useHeadToHead; computePartnerStats; useClub; useBlockUser; useInitiateDmThread; player_ratings per discipline; profiles.profile_visibility + patch_profile_visibility[rpc] visibility-filtered reads of ANOTHER person (match history, achievements, play facts, last-20 attendance + praise for shared-context viewers); useMatchHistory and useAchievements read the viewer's own; [column] profiles 주 종목 and 선호 시간 (no column on profiles or user_preferences; no editor page among the 82 sets them); [rpc] trust record read (신뢰 점수 = 50 + 12-month points held 0..100, none before 5 recorded sessions; per-record points, rule applied, venue/day, streak, praise count): no trust_score column, RPC or domain function exists (profiles has trust_tier only)partial
L3-01 신뢰 기록attendance_records of the person (final_status, strike_value, cancel time vs start, created_at), last 20 counted in 12 months; streak; points per record; 신뢰 점수 + evidence line; praise count; excusal requests + answers; per-record session club + venue + map thumbnail; official: dues + session_payments states, lateness vs own start사유 인정 요청 (reason <=200); official 인정 / 거절attendance-record.repository (findByMember, findStrikesByMember, getSummaryForMember, findByClub, excuseRecord); useMemberStrikeRecords; useAttendanceSummary; useExcuseAttendance (RPC excuse_attendance_record); useMannerTagCounts; dues + session_payments ports[rpc] trust record read (신뢰 점수 = 50 + 12-month points held 0..100, none before 5 recorded sessions; per-record points, rule applied, venue/day, streak, praise count): no trust_score column, RPC or domain function exists (profiles has trust_tier only); [hook] useTrustRecord(userId[, clubId]) over the trust read (existing: useMemberStrikeRecords, useAttendanceSummary, useExcuseAttendance); [table] attendance excuse request (one per attendance_record, reason <=200 chars, answered by host/총무, 거절됨 state): only excuse_attendance_record(p_record_id), an official one-way mark, exists; [column] rsvps.leaves_at, available_until_round, checked_in_at, checked_out_at (rsvps has arrives_at and available_from_round only; no migration adds them); [rpc] attendance record query joined to venue + the cancel-time rule applied (board: "data owed: the venue joins the record query")partial
L3-03 성장 분석30-day club analytics vs prior window; entitlement; role; 모집 계획 (maxMembers, requiredCapacity, weeklyTotalAttendance, planningAttendanceRate)none (read-only)useClubAnalytics (club-analytics.repository); useFeatureGate; useClubContext; useClubMembers / useClubGroups / useClubRegularMeets; club-groups.rules (requiredCapacity, weeklyTotalAttendance)nonecovered
L3-04 기간 기록period matches, days played, opponents, scope filters; another person's record (rp-other) limited by profile_visibilityuseRecordDetailData; usePullToRefresh; useMatchHistory (viewer's own)[rpc] visibility-filtered reads of ANOTHER person (match history, achievements, play facts, last-20 attendance + praise for shared-context viewers); useMatchHistory and useAchievements read the viewer's own (unsure: rp-other (공개 프로필 전체 보기 door) is a variant of this page; own-record variant is covered)partial
L3-05 하루 기록matches of one dateuseMatchHistory (date filter); usePullToRefreshnonecovered
L3-06 선수별 전적every opponent (record, form); every partner; format split 단식/복식; with one person: 상대 record, 같은 편 record, matches togetheruseHeadToHead; computePartnerStats; usePlayerFormatRecords (player_matchups_by_format, player_partner_records); useMatchHistory (partnerId/opponentIds); usePublicProfilenone (unsure: Board "Data owed" (format column) is satisfied by player_matchups_by_format; hook wiring of the format Choice not verified)covered
L3-07 출석 현황per member attendance rate, isFlagged (weighted absences >=3)useClubAttendanceSummary; club-attendance-since-join.repositorynonecovered
L3-08 테니스 여정match notes (matchId, sessionTitle, venueName, mood, body, createdAt) newest first; month's matches + notes count; account plan; AI 요약save / edit a 경기 노트; AI 요약 requestPlanType player + getEffectivePlan + useFeatureGate (gate only); useMatchHistory (month count)[table] match_notes (match_id, author_id, mood good|ok|bad, body <=500; own-row RLS): no table, port or hook exists (practice_logs dropped in 00702); [hook] useMatchNotes(userId, 50) and a save/upsert mutation for a 경기 노트 (no hook, no port); [edge] AI 요약 job/edge function for 테니스 여정 (supabase/functions has none), plus an entitlement key for the player plan gate (getEffectivePlan/useFeatureGate exist; PLAN player exists)missing
L3-09 업적held achievements; catalog filtered by isAchievementShown; progress for quantitative typesuseAchievements; ACHIEVEMENT_TYPES + isAchievementShown; usePlayerStatsDetail; achievement.repository.findByUsernone (unsure: getProgress is named in the board but did not resolve as an export)covered
L3-10 클럽 대항전challenges of the club (대기/진행/지난); both clubs; state word + 승/패 statsaccept (수락); decline (거절)useInterClubChallenges; useClubsByIds; deriveChallengeDisplayStatus; computeChallengeStats; useAcceptChallenge; InterClubChallengeRepositoryPort.decline[hook] useDeclineChallenge (InterClubChallengeRepositoryPort.decline exists; only useAcceptChallenge is a hook)partial
L3-11 회비 관리dues by month in sections 확인 대기/미납/완납; club balance for 장부 Cell; last reminder per member; dues plan for 회비 설정입금 확인; 되돌리기 (unmark); waive; 알림 (one / 모두 알리기); 메모 남기기; 납부 취소useClubDuesData; useClubDuesActions; dues.repository (findByClub, confirmDuesPayment, unmarkPaid, markPaidBulk, updateStatus, getClubSummary); RPCs mark_dues_paid_bulk, unmark_dues_paid; dues.note[table] club_dues_plans (name, amount, cadence incl. one_time/every_n_months/per_session, due rule, grace, proration, applies-to role/조); dues has only month/year, no plan_id; [column] dues.plan_id, dues.period_start, dues.period_end (rows generated per period); [rpc] send reminder for one member / all 미납 members (dues) and for a session fee (참석자 관리), logged by who/when: no port method or RPC (generate_dues_reminders was dropped in 00405); [column] reminder log for 마지막 알림 (who/when) on dues and on session_payments (no column or table); [table] club ledger (opening balance, dated income/expense entries with 분류 + 영수증; a confirmed 회비 = one income entry) with an aggregate read (balance, month spend) and a CSV export: no table, port or hookpartial
L3-13 알림signals of last 30 days not dismissed, grouped per key with actor + count; family per FVK_NOTIF_FAMILIES; 처리할 일 (open requests); bell unreadacknowledge; dismiss + undismiss; 친구 신청 수락/거절 in rowuseNotificationCenter; useActiveSignalsFeed; useAcknowledgeSignals; useDismissSignals / useUndismissSignals (RPC signals_dismiss_batch, signals_undismiss_batch); useOpenRequestSignalIds; useSignalVisibleCounts; signals.dismissed_at/expires_at/resolved_at/acknowledged_atnone (unsure: "one row per group key" has no group_key column on signals (dedup_key, context exist); grouping presumed client-side; board says Lane E §5 lists migrations)covered
L3-14 메시지accepted threads; unread by thread; pending count (cap 99)useDmThreadsByStatus; useDmUnreadByThread; dm_threads.request_statusnone (unsure: /messages/requests route is new; data is the same pending read)covered
L3-15 메시지 요청pending threads (requests)수락 / 거절useDmThreadsByStatus(pending); useDecideDmRequest (RPC decide_dm_request)nonecovered
L3-16 참가비 내역my session_payments (amount, status, submittedAt, refundState) with session day/format/location and club nameuseMySessionPayments; useSessionsByIds; useClubsByIds; sessionPaymentGateState; session_payments.refund_state/submitted_atnone (unsure: Route /profile/fees and MyFeesScreen are new; board says nothing else (due time, method, receipt) exists to read)covered
L3-17 순위표club ranks; friends ranked; regional ranking; national top 50; record value, 7-day ELO change, tier tag; 기간 narrowingshare top fiveuseClubRatingRanks (get_club_rating_ranks) + useClubProfiles; useFriends; useRegionalRanking / useRegionalLeaderboard (get_regional_ranking); profiles.findTopByRating; getTierFromElo; useShareLeaderboard; elo_history[rpc] period (전체/이번 주/이번 달) narrowing on the leaderboard reads (get_club_rating_ranks, get_regional_ranking, get_public_profile_leaderboard, findTopByRating take none) (unsure: recordValue/eloDelta7d did not resolve as exports; 7-day change presumably from elo_history)partial
L3-18 친구 목록friends; incoming + outgoing requestsaccept; decline / cancel; 친구 끊기useFriends; useIncomingFriendRequests; useOutgoingFriendRequests; friendships tablenonecovered
L3-19 친구 찾기profile search (20); friendship state per rowsend friend requestuseProfileSearch; useFriendship; useSendFriendRequestnonecovered
L3-20 차단한 사용자blocked ids + profilesunblockuseBlockedUsers; useProfilesByIds; useUnblockUser; user_blocksnonecovered
L3-21 동의 이력consent history (<=200): purpose, state, createdAt, method, expiresAtuseConsentHistory; user_consent; purpose_catalognonecovered
L3-22 공지사항announcement list from app copy (id, title, date, body, important); opened statemark openedAnnouncementsScreen (static in-app copy, local useState)none (unsure: Board says the list is the app's copy and empty until real; persistence of the opened state and the notice push (/legal/notices/[noticeId]) are not specified)covered
L3-23 앨범approved club photos grouped by origin (session / post); pending photos (approval)none (tiles display-only)useClubMedia (approvalStatus); club_media (session_id, approval_status, visibility); useClubMediaSignedUrlsnone (unsure: club_media has session_id but no post id; the 게시글 door grouping source was not confirmed)covered
L3-24 복식 파트너 추천partner candidates with score breakdown (eloProximity, chemistry, trust, attendance); partner recordsusePartnerCandidates; usePartnerRecords (player_partner_records); strongestPartnerFactor; profiles.global_attendance_ratenonecovered
L3-25 멤버 관리roster by role with effective 조, pin mark, trust score, 출석 주의; join requests (applicants) with prospect basis; recommendable guests승인 / 거절 applicant; 역할 바꾸기; 조 정하기; 내보내기; guest 초대; 클럽 단톡방 만들기useClubMembersData; useProspectAttendanceSummary; useClubAttendanceSummary; useMemberKebabActions; useUpdateMemberRole; RPCs assign_member_group, decide_join_request; join-review.repository.getRecommendableGuests/nominateForMembership; useCreateGroupThread[rpc] trust record read (신뢰 점수 = 50 + 12-month points held 0..100, none before 5 recorded sessions; per-record points, rule applied, venue/day, streak, praise count): no trust_score column, RPC or domain function exists (profiles has trust_tier only) (unsure: Roster trust score (신뢰 84점) has no source until the trust read exists)partial
L3-27 일정 관리club sessions upcoming (open, locked, in_progress) by day; completed sessions paged; attention facts (입금 확인 N건, 게스트 N명)useClubSessions; useCompletedClubSessions; useDaySectionedSessionList; useClubSessionCardRow; canManageSchedulenonecovered
L3-28 신고 관리reports of the club (member, date, category, reason; reporter hidden)resolve 확정 / 기각useClubReports (member-report.repository.findByClub); useResolveMemberReport (resolve); useProfilesByIdsnonecovered
L3-29 테니스장 관리club venuesadd / update / delete venue; 코트 수 / 메모useClubVenues; useCreateVenue / useUpdateVenue / useDeleteVenue; VenueSearchTakeover + useNaverAdoptCorrelation; parseClubVenueCourtCountnonecovered
L3-30 장소 정보 제안 내역my contributions / contributions to review (kind, status, venue, createdAt)useVenueContributions({scope,venueId}); RPC get_venue_contributions; useAuthnonecovered
L3-31 소유권 신청 내역my claims / 운영팀 검토 claimsuseVenueOwnerClaims({scope,venueId,status}); OwnerFrame / useVenueOwnerScope; venue-owner-claim.repository.listnonecovered
L3-32 가입 심사join request message + day; prospect attendance summary, record, ELO, played-with; profile; club join method; votes + time left (멤버 투표)승인 / 거절 (decide); 찬성 / 반대 voteuseMyJoinRequest; useProspectAttendanceSummary; useProfile / useProfilesByIds; useClubContext; useDecideJoinRequest (RPC decide_join_request)[table] club join-vote store for the 멤버 투표 admission rule (votes, deadline, objection) with a cast-vote RPC and a read hook (club_join_request has no vote data)partial
L3-33 제안 상세contribution receipt, evidence, decisionswithdraw; review (승인/거절/추가 정보 필요 + explanation); resubmituseVenueContribution; useWithdrawVenueContribution; useReviewVenueContribution (RPC review_venue_contribution); useAuthnonecovered
L3-34 장소 소유권owner claim, history, scopewithdraw; agree to transfer; supersede / resubmit; relinquish (소유권 끝내기)useVenueOwnerClaim; useVenueOwnerClaimHistory; useVenueOwnerScope; useVenueOwnerClaimMutation (submit, withdraw, setTransferConsent, relinquish, review, revoke)nonecovered
L3-35 참석자 관리rsvps by status; session_payments joined; applications; capabilities; 첫 참석 flag입금 확인 (per row) + 되돌리기; 참가비 면제; 참석 확정으로 올리기; 내보내기; 승인 / 거절 application; 알림 / 모두 알리기 reminder; 정산 나누기useLiveSessionData(includeManagementProfiles); useSessionPayments; usePaymentActions (session-payment port: attest, markPaid, markWaived); useSessionCapabilities; useSessionApplications + useDecideGuestApplication; profiles.total_sessions_confirmed[rpc] send reminder for one member / all 미납 members (dues) and for a session fee (참석자 관리), logged by who/when: no port method or RPC (generate_dues_reminders was dropped in 00405); [hook] 정산 나누기 cost-split write for a session with no fixed fee (sessions.cost_total exists; no hook confirmed) (unsure: Reads names a new bulk-confirm RPC; Taps says the page has no bulk command, so it is not counted)partial
L3-36 참석 확인confirmed rsvps with window + arrival/exit stamps; lateness vs own start (10 min); 확인할 사람 / 도착 groups; unresolved count for 관리 sheet도착 확인 / 모두 확인 (attended); 불참 (no_show); 시간 바꾸기; 되돌리기useLiveSessionData; useConfirmAttendance (RPC confirm_attendance); useSessionAttendance; computeUnresolvedAttendanceCount; rsvps.arrives_at[column] rsvps.leaves_at, available_until_round, checked_in_at, checked_out_at (rsvps has arrives_at and available_from_round only; no migration adds them); [hook] arrival/exit stamp mutation (도착했어요, 먼저 갈게요) writing rsvps.checked_in_at/checked_out_at (only check_in_attendance_gps RPC from 00318 and useArrivalAvailability exist)partial
L3-37 호스트 관리pending applications with names + relative time승인 / 거절useLiveSessionData; useSessionApplications; useProfilesByIds; useDecideGuestApplication (RPC decide_guest_application)nonecovered
L3-38 스코어카드rounds, courts, sets, result state per match; viewer's own matches (team first); session rules + per-match override; tab set by session type; tournament groups / table / bracket; planned start / minutes in play; recap (settled matches + rating change); match notesubmit score (expected revision, idempotency key); 점수 검토 요청; official rule override; 경기 노트; 결과 공유useLiveSessionData; useSessionMatchSets; useSessionMatchScoreCalls; deriveMatchResult (domain); useSubmitScore (RPC submit_match_score); useProposeScoreCorrection (RPC propose_score_correction); useUpdateMatchRulesSnapshot (matches.rules_snapshot); useShareMatch; matches.score_revision, outcome[rpc] submit_match_score expected-revision parameter and the typed refusal score_changed (the RPC takes no revision; matches.score_revision exists); [rpc] move tournament matches into matches (stage/group) and the group-table/bracket/advance reads; retire tournaments.state JSONB + useTournament/useAdvanceTournament/useCancelTournament; [column] matches.stage and group (tournament matches live in tournaments.state JSONB today), matches.scheduled_start (09:30 시작 queue); [table] match_notes (match_id, author_id, mood good|ok|bad, body <=500; own-row RLS): no table, port or hook exists (practice_logs dropped in 00702); [hook] useMatchNotes(userId, 50) and a save/upsert mutation for a 경기 노트 (no hook, no port) (unsure: deriveMatchResultState and useSessionRules (named by the board) did not resolve as exports; closest are deriveMatchResult and matches.rules_snapshot)partial
L3-39 스코어카드 운영scorecard reads + useMatchBoardData; inbox exceptions derived from matches + unsettled payments; queue (matches without a court); minutes from start time; groups -> 본선 draw대신 입력; 검토하기 (keep / change score); 기록에서 제외 / 다시 기록에 넣기; 라운드 추가; 늦게 온 선수 추가 (available_from_round); 팀 바꾸기 / 선수 교체; 대진 다시 짜기; start match on a free court; outcomes walkover/retired/etc.useMatchBoardData; useLiveSessionData; useSessionMatchSets; useSubmitScore (p_outcome accepts walkover, retired, defaulted, suspended, abandoned); useProposeScoreCorrection; rsvps.available_from_round; matches.court_number, started_at, status; useConfirm[rpc] move tournament matches into matches (stage/group) and the group-table/bracket/advance reads; retire tournaments.state JSONB + useTournament/useAdvanceTournament/useCancelTournament; [column] matches.stage and group (tournament matches live in tournaments.state JSONB today), matches.scheduled_start (09:30 시작 queue) (unsure: Board lists 팀 바꾸기 / 대진 다시 짜기 as not in code as board rows (UI, not data))partial
L3-41 게시글post, comments (infinite), reaction summary, author profiles, clubreaction toggle; comment / reply; edit / delete; report; mark readusePostDetail; usePostCommentsInfinite; usePostReactionSummary; useProfilesByIds; useClub; useMarkPostRead; useMarkThreadReadnonecovered
L3-42 DM 스레드thread, messages, chat items, composersend; react; delete (60 s); report / block; accept / decline request; leave groupuseDmThread / useDmThreadData; useDmMessages / useDmLatestMessages; useDmChatItems; useDmComposer; useSendDmMessage; useDmThreadActions; useMarkThreadRead; useToggleDmReaction; useLeaveThreadnonecovered
L3-43 문의하기FAQ copy; support channels; app version / device / account for the mail draftopen mail draftsupportChannels(); ContactScreen static copynonecovered
L3-44 이용약관versioned terms document + historyconfig/legal/documents/terms.ko.ts; config/legal/policy-versions.ts; policy_versions tablenone (unsure: Board draws two versions; shipped document has one)covered
L3-45 개인정보 처리방침versioned privacy document + history내 데이터 받기 (JSON export)config/legal/documents/privacy.ko.ts; policy_versions; useExportUserData + edge function export-user-datanonecovered
L4-01 일정 만들기draft (seeded by regular meet / past session); club preferences; club bank account for fee gatecreate session (club or pickup)create-session-draft; useRegularMeetPrefill / usePastSessionPrefill; useClub; buildSessionRecordRows / recordFromWizardDraft / wizardFeeAmount; useClubBankAccount; session.repository.createnonecovered
L4-02 클럽 만들기live draft; profile region; generated logo; creation receiptcreate club (+logo, region, venues, dues, recruitment)usePersistedWizardDraft (create-club-draft); club-logo; club-setup-receipt; club.repository.create; useClubSetupMutationsnonecovered
L4-03 행사 만들기role (club_events insert policy)create club event (title, date, time, location, link, max attendees, description); club_event_created notificationuseClubRole; useCreateClubEvent; useClubEvents / useClubEvent; club_events (incl. cost_per_person, linked_session_id); signal club_event_created (00514)none (unsure: Route and screen are new; the data layer exists)covered
L4-04 초대 코드로 가입club for an invite code (id only); auth stateresolve code then open 클럽 홈RPC lookup_club_by_invite_code; ClubRepositoryPort.findByInviteCode / joinByInviteCode; useJoinClub; useAuthnone (unsure: useClubByInviteCode (named by the board) does not exist as a hook; JoinClubScreen uses useJoinClub)covered
L4-05 글쓰기role (categories clamp, audience); categoriescreate post + photosuseClubRole; POST_CATEGORIES; useCreatePost; useUploadClubPostMedia / useRemoveClubPostMedia; usePickImagesnonecovered
L4-06 경기 규칙session; capability canEditMatchRules; six scoring formatssave match rules; reset to defaultsuseSessionQuery; useSessionCapabilities; useScoringFormats; MatchRulesSchema; KOREAN_DEFAULT_MATCH_RULES; useUpdateMatchRulesSnapshot; sessions.match_rulesnonecovered
L4-07 장소 정보 제안contribution draft; venue snapshot for a correction; earlier proposalsubmit contribution (new venue, facts, court operations, lifecycle, report)useContributionDraft; useSubmitVenueContribution (RPC submit_venue_contribution); useVenueContributionContext; EvidenceFieldsnonecovered
L4-08 소유권 신청claim attempt; venue snapshotsubmit owner claimuseOwnerClaimAttempt; useVenueOwnerClaimMutation; RPC submit_venue_owner_claim; claim-evidencenonecovered
L4-09 단톡방 만들기friends or seeded profiles; selectioncreate group threaduseFriends; useProfilesByIds; useMemberSelection; useCreateGroupThread (RPC create_group_thread)nonecovered
L4-10 멤버 초대friends + club guests minus thread participantsadd thread membersuseFriends; useDmThreadParticipants; useAddThreadParticipants; useMemberSelectionnone (unsure: Board names useThreadParticipants / useAddThreadMembers; the existing hooks are useDmThreadParticipants / useAddThreadParticipants)covered
L4-11 프로필 수정profile; region keysupdate profile (name, gender, birth year, start year, region); upload / remove avataruseProfile; useUpdateProfile; useUploadAvatar / useRemoveAvatar; usePickImages; SIDO_KEYSnonecovered
L4-12 비밀번호 변경change passworduseChangePassword; AuthServicePort.changePasswordnonecovered
L4-13 회원 탈퇴my clubs with role (blocked page: owners); confirm phrasedelete account (soft delete, release seats, withdraw consents, clear tokens); sign outuseDeleteAccount (profiles.deleteAccountAtomic / softDeleteProfile); useMyClubs; deleteAccount confirm phrasenonecovered
L4-14 회비 설정dues plan (납부 주기, 금액, 납부 기한 일, 대상); first-plan setupsave dues planclubs.dues_amount / dues_cycle / dues_day (single club-level setting); useUpdateClub[table] club_dues_plans (name, amount, cadence incl. one_time/every_n_months/per_session, due rule, grace, proration, applies-to role/조); dues has only month/year, no plan_id; [column] dues.plan_id, dues.period_start, dues.period_end (rows generated per period) (unsure: Board also lists 6 cadences and 대상 (role or 조); only a club-level amount/cycle/day exists)missing
L4-21 입금 계좌club bank account (bank, number, holder)upsert accountclub_bank_accounts; club-bank-account.repository; useClubBankAccount + its upsert mutationnone (unsure: Route /clubs/[clubId]/settings/account is new; board lists a bank-picker list of banks (static))covered
L4-15 설정profile; color scheme (local); OTA update check; developer modesign out; theme mode; rerun touruseProfile; useThemeStore / useColorScheme; checkForAndApplyOtaUpdate; useDeveloperModeStore; AuthServicePort.signOut / useAuthnone (unsure: useColorSchemeStore and useSignOut (board names) do not exist by those names)covered
L4-16 연결된 계정connected identities (provider, nickname/email)link provider; unlink provider (server refuses the last)(none)[hook] getUserIdentities / linkIdentity / unlinkIdentity on AuthServicePort + hooks (port has signInWithOAuth, signInWithEmail, signOut, changePassword only); [config] supabase/config.toml configures only [auth.external.apple]; Kakao, Naver (custom) and Google providers are not configured locally; enable_manual_linking = false (needed by 연결된 계정)missing
L4-17 공개 범위 설정profile_visibility merged over DEFAULT_VISIBILITY; preview per audience (device)patch one visibility fielduseProfile; usePatchProfileVisibility (RPC patch_profile_visibility)nonecovered
L4-18 알림 설정signal preferences per family (quiet hours); master push + message preview; per-club levelbatch update signal preferences; update preferencesuseSignalPreferences / useUpdateSignalPreferences (signal_preferences.quiet_hours); usePreferences / useUpdatePreferences (user_preferences); FVK_NOTIF_FAMILIESnone (unsure: Per-club level (전체/공지만/끔) storage not verified: signal_preferences is keyed by category, not club)covered
L4-19 동의 관리consents by purpose, jurisdiction channel; purpose labelsgrant; revokeuseUserConsents; useGrantConsent; useRevokeConsent; user_consent / purpose_catalognone (unsure: Board names useConsents; the existing hook is useUserConsents)covered
L4-20 클럽 설정club, role, bank account; regular meets + groups for planning facts; club mediaupdate one club field; upload banner / logo; transfer ownership; archive / unarchive; invite code shareuseClub; useClubRole; useUpdateClub; useClubBankAccount; useClubRegularMeets (+ create/update/delete); useClubGroups (+ CRUD); useManageClubMedia / useUpdateClubMedia / useArchiveClubMedia; useTransferClubOwnership / useArchiveClub / useUnarchiveClub; club_play_regionsnone (unsure: The owed editors 정기 일정, 조, 앨범 관리 have no page code but their hooks exist; the 회비 설정 door depends on dues_plans (see L4-14))covered
L3-46 장부ledger entries by month (infinite); balance over all entries; month spendadd / edit / delete expense; CSV export(none)[table] club ledger (opening balance, dated income/expense entries with 분류 + 영수증; a confirmed 회비 = one income entry) with an aggregate read (balance, month spend) and a CSV export: no table, port or hookmissing
L3-47 대진 짜기session, format, court count; confirmed RSVPs less unsettled fee payers; arrival rounds and times; attendance viability; round preview; session rules; generation style; tournament strategy inputsgenerate round (저장); create tournament; arrival marks (available_from_round, arrives_at); saved presetuseGenerateRoundInputs; useRoundPreview; useGenerateRound (RPC generate_session_round); computeAttendanceViability; partitionSettledMatchPool; useCreateTournament; rsvps.available_from_round / arrives_at; sessions.generation_style_id; GenerationPolicy entity (spine-only)[column] saved generation preset 소셜/실력별/고정/랜덤 on sessions (board: only the three score-driven generation_style_id values persist; GenerationPolicy is spine-only, no persisted axes); [rpc] move tournament matches into matches (stage/group) and the group-table/bracket/advance reads; retire tournaments.state JSONB + useTournament/useAdvanceTournament/useCancelTournament; [column] persisted GenerationPolicy axes the page offers beyond recording (host-selected pool 선택한 사람만, 만드는 시점, 확정 시점): generation-policy.entity.ts derives an effective policy and persists no policy column (unsure: useSessionRules (named by the board) did not resolve; 대진 짜기 reads rules through the session)partial

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