-
Notifications
You must be signed in to change notification settings - Fork 3
[feature] AB테스트를 위한 실험 구조 구축 #1358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
seongwon030
merged 15 commits into
develop-fe
from
feature/#1354-mixpanel-experiment-hook-MOA-763
Mar 28, 2026
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
6e01671
feat: 프론트 로컬 실험 배정 구조 추가
seongwon030 aceea44
docs: 믹스패널 리포트 문서 추가
seongwon030 38d02f3
docs: 프론트엔드 A/B 실험 시스템 가이드 문서 추가
seongwon030 26cf63d
docs: 실험 문서 추가
seongwon030 b89c6cc
refactor: 실험 상수명 변경
seongwon030 002a5fc
fix: lint error
seongwon030 8740f27
fix: 실험 key변경
seongwon030 7a445cb
fix: localStorage 읽기/쓰기 방어 로직 강화
seongwon030 dab1c24
cods: CLAUDE.md 업데이트
seongwon030 3903db8
chore: gitignore에서 CLAUDE관련 제거
seongwon030 0c6a5c0
docs: e2e 커맨드 추가
seongwon030 328f0f0
docs: API훅 에이전트 추가
seongwon030 3f0780d
refactor: getVariant의 useMemo제거
seongwon030 864a64c
Update CLAUDE.md
seongwon030 fef5a9b
fix: e2e무한재시도 방지 내용 문서에 추가
seongwon030 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,5 @@ | |
|
|
||
| .env | ||
|
|
||
| CLAUDE.md | ||
| .claude | ||
|
|
||
| dailyNote/ | ||
|
|
||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| # 프론트엔드 실험(A/B Test) 시스템 가이드 | ||
|
|
||
| ## 개요 | ||
|
|
||
| `frontend/src/experiments/`는 클라이언트 사이드 A/B 실험을 위한 경량 구조다. | ||
| 외부 서비스 없이 localStorage 기반으로 배리언트를 배정하고, React 훅으로 컴포넌트에서 간편하게 사용할 수 있다. | ||
|
|
||
| ## 파일 구성 | ||
|
|
||
| ``` | ||
| frontend/src/experiments/ | ||
| ├── types.ts # 실험 관련 타입 정의 | ||
| ├── definitions.ts # 실험 목록 정의 (여기만 수정하면 됨) | ||
| ├── ExperimentRepository.ts # 배리언트 배정 및 조회 로직 | ||
| └── initializeExperiments.ts # 앱 시작 시 일괄 배정 | ||
|
|
||
| frontend/src/hooks/Experiment/ | ||
| └── useExperimentVariant.ts # 컴포넌트에서 사용하는 React 훅 | ||
| ``` | ||
|
|
||
| ## 동작 원리 | ||
|
|
||
| 1. **앱 시작 시** `initializeExperiments()`가 `ALL_EXPERIMENTS`를 순회하며 각 실험의 배리언트를 배정한다. | ||
| 2. 이미 배정된 실험은 재배정하지 않는다. (같은 유저는 항상 같은 배리언트를 본다) | ||
| 3. 배정 결과는 `localStorage`의 `moadong_experiments` 키에 JSON으로 저장된다. | ||
| 4. 컴포넌트에서는 `useExperimentVariant` 훅으로 배리언트를 읽어 분기한다. | ||
|
|
||
seongwon030 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ## 새 실험 추가 방법 (3단계) | ||
|
|
||
| ### 1단계 — `definitions.ts`에 실험 정의 추가 | ||
|
|
||
| ```typescript | ||
| // frontend/src/experiments/definitions.ts | ||
|
|
||
| export const myNewExperiment = { | ||
| key: "my_new_experiment_v1", // 전역 고유값. 중복 금지 | ||
| variants: ["A", "B"] as const, | ||
| defaultVariant: "A", // 배정 실패 또는 초기화 시 기본값 | ||
| weights: { | ||
| A: 50, | ||
| B: 50, | ||
| }, | ||
| } satisfies ExperimentDefinition<"A" | "B">; | ||
|
|
||
| // ALL_EXPERIMENTS 배열에도 추가 | ||
| export const ALL_EXPERIMENTS = [ | ||
| mainBannerExperiment, | ||
| applyButtonCopyExperiment, | ||
| myNewExperiment, // ← 여기 | ||
| ] as const; | ||
| ``` | ||
|
|
||
| ### 2단계 — 컴포넌트에서 배리언트 분기 | ||
|
|
||
| ```typescript | ||
| import { useExperimentVariant } from '@/hooks/Experiment/useExperimentVariant'; | ||
| import { myNewExperiment } from '@/experiments/definitions'; | ||
|
|
||
| const MyComponent = () => { | ||
| const variant = useExperimentVariant(myNewExperiment); | ||
|
|
||
| return variant === 'B' ? <NewVersion /> : <DefaultVersion />; | ||
| }; | ||
| ``` | ||
|
|
||
| ### 3단계 — Mixpanel 이벤트에 배리언트 속성 포함 (권장) | ||
|
|
||
| 실험 결과를 분석하려면 이벤트 전송 시 배리언트 값을 속성으로 넘겨야 한다. | ||
|
|
||
| ```typescript | ||
| import { trackEvent } from "@/utils/mixpanel"; // 실제 트래킹 유틸 경로 참고 | ||
|
|
||
| trackEvent("클릭 이벤트", { | ||
| experiment_key: myNewExperiment.key, | ||
| experiment_variant: variant, | ||
| }); | ||
| ``` | ||
|
|
||
| ## weights(가중치) 설정 | ||
|
|
||
| `weights`를 생략하면 균등 배분된다. | ||
| 비율을 조정하고 싶을 때만 명시한다. | ||
|
|
||
| ```typescript | ||
| // 10%만 B를 보는 실험 | ||
| weights: { | ||
| A: 90, | ||
| B: 10, | ||
| }, | ||
| ``` | ||
|
|
||
| ## 배리언트 초기화 (개발/QA용) | ||
|
|
||
| 브라우저 콘솔에서 실행하면 배정이 초기화된다. | ||
|
|
||
| ```javascript | ||
| localStorage.removeItem("moadong_experiments"); | ||
| location.reload(); | ||
| ``` | ||
|
|
||
| 또는 코드에서 직접 호출: | ||
|
|
||
| ```typescript | ||
| import { experimentRepository } from "@/experiments/ExperimentRepository"; | ||
| experimentRepository.resetAssignments(); | ||
| ``` | ||
|
|
||
| ## key 네이밍 규칙 | ||
|
|
||
| - 형식: `{실험_대상}_{버전}` (소문자 snake_case) | ||
| - 예시: `main_banner_v1`, `apply_button_copy_v1` | ||
| - 실험이 종료되고 새로 시작할 때는 버전을 올린다: `main_banner_v2` | ||
| - 이미 배포된 key는 절대 재사용하지 않는다. (기존 유저 배정 오염 방지) | ||
|
|
||
| ## 실험 종료 후 정리 | ||
|
|
||
| 1. `definitions.ts`에서 해당 실험 상수와 `ALL_EXPERIMENTS` 항목을 제거한다. | ||
| 2. 채택된 배리언트 코드만 남기고 분기 로직을 제거한다. | ||
| 3. 버려진 배리언트 코드를 삭제한다. | ||
|
|
||
| ## 현재 운영 중인 실험 | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| # Mixpanel MCP 관리자 주간 리포트 고정 프롬프트 세트 | ||
|
|
||
| ## 목적 | ||
|
|
||
| - 관리자(Admin) 사용 흐름을 주 단위로 모니터링한다. | ||
| - 설정/수정 관련 행동 데이터를 표준 템플릿으로 축적한다. | ||
|
|
||
| ## 운영 원칙 | ||
|
|
||
| - 기간은 항상 `지난주 (월요일 00:00 ~ 일요일 23:59, KST)`로 고정한다. | ||
| - 비교 기준은 항상 `직전 주`로 고정한다. | ||
| - 관리자 이벤트에는 "클릭" 위주 이벤트가 많으므로, 저장 성공/실패는 별도 백엔드 로그와 함께 해석한다. | ||
| - 결과는 아래 "리포트 템플릿" 순서로 정리한다. | ||
|
|
||
| ## 고정 프롬프트 8개 (moadong ADMIN_EVENT 치환) | ||
|
|
||
| 1. `지난주(월~일, Asia/Seoul) 관리자 핵심 KPI를 보여줘. 다음 이벤트를 기준으로 유저 수와 이벤트 수를 각각 보여줘: "로그인페이지 Visited", "로그인 버튼클릭", "사이드바 탭 클릭", "동아리 기본 정보 수정 버튼클릭", "동아리 모집 정보 수정 버튼클릭", "활동 사진 업로드 버튼클릭", "비밀번호 변경 버튼클릭". 직전 주 대비 증감률(%) 포함.` | ||
|
|
||
| 2. `지난주 관리자 진입 퍼널을 보여줘: "로그인페이지 Visited" -> "로그인 버튼클릭" -> "동아리 기본 정보 수정 페이지 Visited"(또는 다른 관리자 탭 Visited). 단계별 전환율/이탈률과 직전 주 대비 변화를 계산해줘.` | ||
|
|
||
| 3. `지난주 사이드바 탭 이동 패턴을 분석해줘. "사이드바 탭 클릭" 이벤트의 tab_name 속성 기준으로 탭별 클릭 수, 유저 수, 전주 대비 증감을 표로 보여줘.` | ||
|
|
||
| 4. `지난주 동아리 기본 정보 수정 관련 행동을 요약해줘: "동아리 기본 정보 수정 버튼클릭", "분류/분과/자유태그 선택 버튼클릭", "자유태그 입력 초기화 버튼클릭", "SNS 링크 입력 초기화 버튼클릭". 이벤트별 유저 수/이벤트 수와 전주 대비 변화 포함.` | ||
|
|
||
| 5. `지난주 모집 정보 수정 관련 행동을 요약해줘: "동아리 모집 정보 수정 버튼클릭", "상시모집 버튼클릭", "모집 시작 날짜 변경", "모집 종료 날짜 변경", "모집 대상 입력 초기화 버튼클릭", "소개글 미리보기/편집 버튼클릭". 이벤트별 추세와 함께 보여줘.` | ||
|
|
||
| 6. `지난주 이미지 자산 편집 행동을 비교해줘: "동아리 커버 업로드 버튼클릭", "동아리 커버 초기화 버튼클릭", "동아리 로고 업로드 버튼클릭", "동아리 로고 초기화 버튼클릭", "활동 사진 업로드 버튼클릭", "활동 사진 삭제 버튼클릭". 업로드/초기화/삭제 비율도 계산해줘.` | ||
|
|
||
| 7. `지난주 계정 보안 관련 행동을 보여줘: "비밀번호 변경 버튼클릭", "새 비밀번호 입력 초기화 버튼클릭", "확인 비밀번호 입력 초기화 버튼클릭". 전주 대비 증감과 이상 패턴(초기화 클릭 과다 등)을 코멘트해줘.` | ||
|
|
||
| 8. `위 1~7 결과를 바탕으로 이번 주 관리자 UX 개선 액션 3개를 제안해줘. 각 액션마다 목표 KPI, 기대효과, 검증방법(A/B 또는 관찰지표), 우선순위를 포함해줘.` | ||
|
|
||
| ## 리포트 템플릿 (복붙용) | ||
|
|
||
| ```md | ||
| # 관리자 주간 리포트 (YYYY-W##) | ||
|
|
||
| ## 1) 한 줄 요약 | ||
|
|
||
| - | ||
|
|
||
| ## 2) KPI 스냅샷 (지난주 vs 직전 주) | ||
|
|
||
| - 로그인페이지 Visited (users/events): | ||
| - 로그인 버튼클릭 (users/events): | ||
| - 사이드바 탭 클릭 (users/events): | ||
| - 동아리 기본 정보 수정 버튼클릭 (users/events): | ||
| - 동아리 모집 정보 수정 버튼클릭 (users/events): | ||
| - 활동 사진 업로드 버튼클릭 (users/events): | ||
| - 비밀번호 변경 버튼클릭 (users/events): | ||
|
|
||
| ## 3) 핵심 변화 3가지 | ||
|
|
||
| 1. | ||
| 2. | ||
| 3. | ||
|
|
||
| ## 4) 원인 가설 | ||
|
|
||
| - | ||
|
|
||
| ## 5) 이번 주 액션 3개 | ||
|
|
||
| 1. 액션: | ||
| 목표 KPI: | ||
| 기대효과: | ||
| 검증방법: | ||
| 우선순위: | ||
| 2. 액션: | ||
| 목표 KPI: | ||
| 기대효과: | ||
| 검증방법: | ||
| 우선순위: | ||
| 3. 액션: | ||
| 목표 KPI: | ||
| 기대효과: | ||
| 검증방법: | ||
| 우선순위: | ||
|
|
||
| ## 6) 리스크 / 확인 필요 | ||
|
|
||
| - | ||
| ``` | ||
|
|
||
| ## 커스터마이즈 체크리스트 | ||
|
|
||
| - 실제 운영 탭 이름 확인: `사이드바 탭 클릭`의 `tab_name` 속성 값 | ||
| - 수정 성공 지표 보강 여부 결정: 클릭 이벤트 외 백엔드 성공 로그 연동 | ||
| - 관리자 계정 식별 기준 확정 (distinct_id 또는 사용자 속성) | ||
| - 페이지 체류 지표 활용 여부 결정 (`로그인페이지 Duration`, `동아리 기본 정보 수정 페이지 Duration` 등) | ||
|
|
||
| ## 실행 순서 권장 | ||
|
|
||
| 1. 프롬프트 1~7 실행 후 사실 데이터 확정 | ||
| 2. 프롬프트 8 실행으로 액션 도출 | ||
| 3. 템플릿에 결과 이관 후 팀 공유 |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # Mixpanel 리포팅 문서 인덱스 | ||
|
|
||
| ## 개요 | ||
|
|
||
| moadong에서 Mixpanel MCP로 주간 리포트를 만들 때 사용하는 문서 모음이다. | ||
|
|
||
| ## 문서 바로가기 | ||
|
|
||
| - 사용자(학생) 흐름 리포트: [mixpanel-weekly-report-prompts.md](./mixpanel-weekly-report-prompts.md) | ||
| - 관리자(Admin) 흐름 리포트: [mixpanel-admin-weekly-report-prompts.md](./mixpanel-admin-weekly-report-prompts.md) | ||
|
|
||
| ## 최근 주간 리포트 | ||
|
|
||
| - 2026-W12 사용자 리포트: [2026-W12-user-mixpanel-report.md](./weekly-reports/2026-W12-user-mixpanel-report.md) | ||
|
|
||
| ## 주간 실행 순서 | ||
|
|
||
| 1. Mixpanel MCP 연결 상태 확인 | ||
| - `codex mcp list` | ||
| 2. 사용자 리포트 실행 | ||
| - `docs/mixpanel-weekly-report-prompts.md`의 고정 프롬프트 1~8 순서대로 실행 | ||
| 3. 관리자 리포트 실행 | ||
| - `docs/mixpanel-admin-weekly-report-prompts.md`의 고정 프롬프트 1~8 순서대로 실행 | ||
| 4. 각 템플릿에 결과 이관 후 팀 공유 | ||
|
|
||
| ## 운영 규칙 | ||
|
|
||
| - 기간 기준 고정: `지난주 (월요일 00:00 ~ 일요일 23:59, KST)` | ||
| - 비교 기준 고정: `직전 주` | ||
| - 이벤트 누락/속성 불일치 시 추정하지 않고 리스크로 명시 | ||
|
|
||
| ## 업데이트 규칙 | ||
|
|
||
| - 신규 이벤트 추가 시, 먼저 `frontend/src/constants/eventName.ts`를 기준으로 이벤트명을 확인한다. | ||
| - 이벤트/속성 변경 시 사용자 문서와 관리자 문서를 함께 갱신한다. | ||
| - 퍼널 정의가 바뀌면 템플릿 KPI 항목도 함께 수정한다. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # Mixpanel MCP 주간 리포트 고정 프롬프트 세트 | ||
|
|
||
| ## 목적 | ||
|
|
||
| - 매주 같은 기준으로 KPI를 조회하고, 비교와 해석을 일관되게 남긴다. | ||
| - 리포트 품질을 사람 숙련도에 의존하지 않고 템플릿으로 표준화한다. | ||
|
|
||
| ## 운영 원칙 | ||
|
|
||
| - 기간은 항상 `지난주 (월요일 00:00 ~ 일요일 23:59, KST)`로 고정한다. | ||
| - 비교 기준은 항상 `직전 주`로 고정한다. | ||
| - 지표가 비어 있거나 이벤트명이 불일치하면, 추정하지 말고 누락 항목으로 명시한다. | ||
| - 결과는 아래 "리포트 템플릿" 순서로 정리한다. | ||
|
|
||
| ## 고정 프롬프트 8개 (moadong 이벤트명 1차 치환) | ||
|
|
||
| 1. `지난주(월~일, Asia/Seoul) 핵심 KPI를 보여줘. 이벤트는 정확히 다음 기준으로 집계해줘: "MainPage Visited"(유저 수), "ClubDetailPage Visited"(유저 수), "ApplicationFormPage Visited"(유저 수), "Application Form Submitted"(이벤트 수/유저 수). 직전 주 대비 증감률(%)도 포함해줘.` | ||
|
|
||
| 2. `지난주(월~일, Asia/Seoul) 지원 퍼널 전환율을 보여줘: "MainPage Visited" -> "ClubCard Clicked" -> "ClubDetailPage Visited" -> "Club Apply Button Clicked" -> "ApplicationFormPage Visited" -> "Application Form Submitted". 각 단계 전환율/이탈률과 직전 주 대비 변화를 함께 보여줘.` | ||
|
|
||
| 3. `지난주 "ClubCard Clicked" 사용자 cohort 기준 D1, D7 재방문율을 보여줘. 재방문 기준 이벤트는 "MainPage Visited"로 계산하고, 최근 4주 추세를 표로 정리해줘.` | ||
|
|
||
| 4. `지난주 유입 채널 성과를 비교해줘. "MainPage Visited"의 referrer 속성 기준으로 채널을 나누고, 각 채널별 "ClubCard Clicked", "Club Apply Button Clicked", "Application Form Submitted" 전환율을 계산해줘.` | ||
|
|
||
| 5. `지난주 디바이스/플랫폼별 성과를 비교해줘. Mixpanel 기본 디바이스 속성($os, $browser)을 사용해서 "MainPage Visited" -> "Application Form Submitted" 전환율을 iOS/Android/Web 기준으로 정리해줘.` | ||
|
|
||
| 6. `직전 주 대비 "Application Form Submitted" 변화 원인을 이벤트 기여도로 분해해줘. 후보 이벤트는 "MainPage Visited", "ClubCard Clicked", "Club Apply Button Clicked", "ApplicationFormPage Visited"로 제한하고 증가/감소 기여 Top 5를 해석해줘.` | ||
|
|
||
| 7. `지난주 이탈 구간을 진단해줘. "Club Apply Button Clicked" 대비 "ApplicationFormPage Visited", "ApplicationFormPage Visited" 대비 "Application Form Submitted" 전환율을 클럽별(club_id 또는 club_name 속성)로 비교해서 하위 10개를 보여줘.` | ||
|
|
||
| 8. `위 1~7 결과를 기반으로 이번 주 실행 액션 3개를 제안해줘. 각 액션마다 목표 KPI(예: "Application Form Submitted" 유저 수), 기대효과, 검증방법(A/B 또는 관찰지표), 우선순위를 포함해줘.` | ||
|
|
||
| ## 리포트 템플릿 (복붙용) | ||
|
|
||
| ```md | ||
| # 주간 리포트 (YYYY-W##) | ||
|
|
||
| ## 1) 한 줄 요약 | ||
|
|
||
| - | ||
|
|
||
| ## 2) KPI 스냅샷 (지난주 vs 직전 주) | ||
|
|
||
| - MainPage Visited (users): | ||
| - ClubDetailPage Visited (users): | ||
| - ApplicationFormPage Visited (users): | ||
| - Application Form Submitted (events/users): | ||
| - D1/D7 재방문율 (ClubCard Clicked cohort): | ||
|
|
||
| ## 3) 핵심 변화 3가지 | ||
|
|
||
| 1. | ||
| 2. | ||
| 3. | ||
|
|
||
| ## 4) 원인 가설 | ||
|
|
||
| - | ||
|
|
||
| ## 5) 이번 주 액션 3개 | ||
|
|
||
| 1. 액션: | ||
| 목표 KPI: | ||
| 기대효과: | ||
| 검증방법: | ||
| 우선순위: | ||
| 2. 액션: | ||
| 목표 KPI: | ||
| 기대효과: | ||
| 검증방법: | ||
| 우선순위: | ||
| 3. 액션: | ||
| 목표 KPI: | ||
| 기대효과: | ||
| 검증방법: | ||
| 우선순위: | ||
|
|
||
| ## 6) 리스크 / 확인 필요 | ||
|
|
||
| - | ||
| ``` | ||
|
|
||
| ## 커스터마이즈 체크리스트 | ||
|
|
||
| - 사용자 핵심 이벤트 확정: `MainPage Visited`, `ClubCard Clicked`, `ClubDetailPage Visited`, `Club Apply Button Clicked`, `ApplicationFormPage Visited`, `Application Form Submitted` | ||
| - KPI 정의를 팀 문서와 일치시킴 (예: 제출 KPI를 이벤트 수로 볼지 유저 수로 볼지) | ||
| - 채널 분류 기준 확정 (`referrer` 우선, 필요 시 UTM 속성 추가 수집) | ||
| - 플랫폼 구분 기준 확정 (`$os`, `$browser` 사용 여부) | ||
| - 관리자 리포트를 별도로 운영할지 결정 (`로그인 버튼클릭`, `동아리 모집 정보 수정 버튼클릭` 등 ADMIN_EVENT 기반) | ||
|
|
||
| ## 실행 순서 권장 | ||
|
|
||
| 1. 프롬프트 1~7 실행 후 사실 데이터 확정 | ||
| 2. 프롬프트 8 실행으로 액션 도출 | ||
| 3. 템플릿에 결과 이관 후 팀 공유 |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.