GitHub 트라이지
GitHub 저장소의 오픈된 이슈와 풀 리퀘스트(PR)를 트라이지합니다. 선택적으로 준비된 PR을 먼저 처리하고(통과하는 봇 PR과 유지보수자가 승인한 PR을 병합하고, 아직 검토되지 않은 PR은 검토), 이미 해결된 이슈는 (해결한 PR이나 커밋을 인용하는 코멘트와 함께) 종료하며, 이슈와 이를 수정하는 보류 중인 PR이 서로 참조하도록 하고, 아직 해결되지 않은 모든 이슈에 로컬 전용 우선순위와 변경 규모 추정치를 할당합니다.
사용 시기
- 사용자가
/github-triage를 실행하여 저장소의 오픈 이슈와 풀 리퀘스트를 정리하거나 검토할 때. - 이슈 백로그가 표류했을 때: 해결된 작업이 열려 있거나, 수정 사항이 이슈를 닫지 않고 적용되었거나, 이슈와 연결되지 않은 PR이 진행 중일 때.
- 준비된 PR이 쌓였을 때 (통과하는 의존성 업데이트, 승인 및 통과 상태인 PR) 또는 PR이 검토되지 않고 방치되었을 때.
사용하지 말아야 할 시기
- 자동으로 호출하지 마십시오. 이 스킬은 되돌릴 수 없는 GitHub 쓰기 작업(PR 병합, 이슈 종료, 코멘트 게시)을 수행하며 명시적인 호출 시에만 실행됩니다.
- GitHub에 우선순위/작업량 레이블을 적용하는 데 사용하지 마십시오. 우선순위와 규모는 로컬로만 표시되며 절대 게시되지 않습니다 (안전 규칙 참조).
- 사람의 최종 병합 결정을 대체하는 용도로 사용하지 마십시오. 모든 병합은 명시적인 승인을 위해 제안되며, 자율적으로 수행되지 않습니다.
핵심 원칙
- 쓰기 작업은 승인을 받습니다. 전체 트라이지를 먼저 계산하고, 병합을 포함한 모든 제안된 쓰기 작업을 검토를 위해 제시하며, 사용자가 승인할 때까지 아무것도 실행하지 않습니다.
- 종료 전 증거 제시. 구체적이고 인용된 이유(병합된 PR 또는 기본 브랜치의 커밋) 없이 이슈를 닫지 마십시오. 증거가 약하거나 모호한 경우 이슈를 미해결 상태로 두고 검토 플래그를 지정합니다.
- 우선순위와 규모는 로컬에 유지됩니다. 이는 사용자를 위한 내부 계획 지원 도구이며, GitHub에 기록되지 않습니다.
워크플로우
0단계: 대상 저장소 선택
gh auth status # gh 인증 확인
git rev-parse --is-inside-work-tree # 현재 작업 디렉토리가 git 저장소인가?
git remote -v # 원격 저장소 목록 확인
원격 저장소 중 별개의 GitHub 호스팅 저장소 집합을 결정합니다. 원격 저장소의 URL 호스트가 github.com이고 다음 형식 중 하나인 경우 GitHub 호스팅입니다.
https://github.com/OWNER/REPO(.git)[email protected]:OWNER/REPO(.git)ssh://[email protected]:OWNER/REPO(.git)
각각을 OWNER/REPO로 정규화하고 중복을 제거합니다 (포크 설정에는 origin과 upstream이 다른 GitHub 저장소를 가리킬 수 있음; 동일한 OWNER/REPO를 가리키는 여러 원격 저장소는 한 번만 계산).
선택 규칙:
| 상황 | 조치 |
|---|---|
| 정확히 하나의 별개 GitHub 저장소 | 기본값으로 사용 — 프롬프트 표시하지 않음. |
| Git 저장소가 아니거나 GitHub 원격 저장소가 0개 | 사용자에게 트라이지할 OWNER/REPO를 요청. |
| 둘 이상의 별개 GitHub 저장소 | AskUserQuestion을 사용하여 사용자가 OWNER/REPO를 선택하도록 함. |
GitHub Enterprise 호스트는 안정적으로 자동 감지할 수 없습니다. 사용자가 GHE 인스턴스에서 작업하는 경우
OWNER/REPO를 요청하고GH_HOST를 설정하거나gh의 설정된 호스트를 사용하도록 합니다. 계속 진행하기 전에 확인된 저장소를 사용자에게 다시 알립니다.
결과를 REPO="OWNER/REPO"로 저장하고 모든 gh 호출에 -R "$REPO"를 전달합니다. 사용 전에 ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$에 대해 유효성을 검사하여 잘못된 형식이나 유해한 원격 URL이 명령에 유입되지 않도록 합니다.
1단계: 이슈 및 컨텍스트 수집
# 오픈 이슈 (gh issue list는 기본적으로 PR을 제외함)
gh issue list -R "$REPO" --state open --limit 1000 \
--json number,title,body,labels,assignees,comments,reactionGroups,createdAt,updatedAt,url
# 오픈 PR — "보류 중인 수정"(이슈 단계) 및 PR 트라이지(2단계)의 후보
gh pr list -R "$REPO" --state open --limit 1000 \
--json number,title,body,author,isDraft,reviewDecision,latestReviews,\
mergeable,mergeStateStatus,statusCheckRollup,labels,createdAt,headRefName,url,closingIssuesReferences
# 최근 병합된 PR — "이미 해결됨"의 후보
gh pr list -R "$REPO" --state merged --limit 300 \
--json number,title,body,mergedAt,url,closingIssuesReferences
closingIssuesReferences는 PR이 종료하도록 연결된 이슈를 나열합니다 — PR 본문의 GitHub 종료 키워드(close/closes/closed, fix/fixes/fixed, resolve/resolves/resolved) 또는 수동 UI 링크로 채워집니다. 이것이 가장 강력한 신호입니다. 이것이 다루지 않는 이슈의 경우 기본 브랜치의 커밋 메시지도 검색합니다. 선택한 저장소에서 기본 브랜치를 권위적으로 확인합니다 (로컬 origin/HEAD는 설정되지 않았거나 잘못된 원격 저장소를 가리킬 수 있음):
default_branch=$(gh repo view "$REPO" --json defaultBranchRef --jq .defaultBranchRef.name)
# 이슈 번호를 고정하여 #12가 #120, #123, …과 일치하지 않도록 함
git log --oneline "origin/$default_branch" \
| grep -iE "(close|fix|resolve)[sd]? +#<N>([^0-9]|$)"
2단계: 오픈 풀 리퀘스트 트라이지 (선택 사항)
이슈보다 먼저 PR을 처리합니다: 여기서 준비된 PR을 병합하면 이슈 단계의 "이미 해결됨" 확인이 해당 병합으로 방금 적용된 작업을 볼 수 있습니다.
오픈 PR이 없으면 이 단계를 건너뜁니다. 그렇지 않으면 오픈 PR을 요약하고 사용자에게 지금 PR을 처리할지 여부를 묻습니다 (AskUserQuestion: PR 처리 / 이슈로 건너뛰기). 건너뛰면 바로 이슈 분류로 이동합니다.
각 오픈 PR을 리뷰, CI 및 병합 상태에 따라 분류합니다. 아래 필드 형태는 gh pr ... --json이 실제로 반환하는 것입니다 — 직관이 아닌 이에 의존하십시오:
- 병합 준비 완료 =
mergeable == "MERGEABLE"그리고mergeStateStatus == "CLEAN"그리고 CI가 차단하지 않음 (아래).CLEAN은 GitHub 서버 측의 "충돌 없음, 뒤쳐지지 않음, 초안 아님, 필수 검사 통과" 평가입니다. 다른mergeStateStatus(BEHIND,UNSTABLE,BLOCKED,DIRTY,DRAFT, …)는 준비되지 않음입니다.mergeable == "UNKNOWN"(GitHub는 게으르게 병합 가능성을 재계산하며, 특히 다른 병합 직후)은 준비되지 않음으로 처리 — 잠시 다시 폴링하거나 건너뜀; 절대 병합하지 마십시오. - CI 차단하지 않음 —
statusCheckRollup을 스캔하고 (__typename키) 실제 문제가 있는 경우에만 PR을 거부합니다: 하드 실패(conclusion이FAILURE/CANCELLED/TIMED_OUT/ACTION_REQUIRED/STARTUP_FAILURE/STALE인CheckRun, 또는state가FAILURE/ERROR인StatusContext), 또는 아직 실행 중(status가QUEUED/IN_PROGRESS/WAITING/PENDING인CheckRun, 또는state가PENDING/EXPECTED인StatusContext— 기다리세요, 아직 병합하지 마십시오).SUCCESS,NEUTRAL및SKIPPED는 괜찮음이며 차단해서는 안 됩니다 (예: 의존성 업데이트에서NEUTRAL을 보고하는 CodeQL 실행 — 통과하는 Dependabot PR은CLEAN이고 CodeQL 검사가NEUTRAL임). 빈 롤업은 CI 없음 — 별개의 상태이며 준비된 것으로 처리하지 않습니다.mergeStateStatus == "CLEAN"은 이미 필수 검사 상태를 반영합니다. 롤업을 사용하여 실패/보류 중인 비필수 검사를 포착합니다. - 봇/자동화 =
author.is_bot == true. 자동 병합 허용 목록을author.login과 비교합니다.gh의 렌더링을 정규화합니다 — 선행app/와 후행[bot]을 제거합니다 (gh는 일부 버전에서 Dependabot을app/dependabot으로 반환하고 다른 버전에서는dependabot[bot]으로 반환함; 둘 다dependabot으로 정규화). 기본적으로 허용 목록:dependabot,renovate및 사용자가 지정한 추가 항목. 허용 목록에 없는 봇의 통과 PR은 보고되며 병합 대상으로 제공되지 않습니다. - 유지보수자 승인 =
latestReviews에state == "APPROVED"이고authorAssociation이OWNER/MEMBER/COLLABORATOR이며author.login이 PR 작성자가 아닌 항목이 있습니다.reviewDecision == "APPROVED"만 사용하지 마십시오: 이는 브랜치 보호 기반이며 필수 검토 규칙이 없는 저장소에서는null이므로 과도하게 신뢰하고(쓰기 액세스를 확인할 수 없음) 진정한 승인을 놓칩니다. - 검토되지 않음 =
latestReviews에 PR 작성자 이외의 사람으로부터APPROVED/CHANGES_REQUESTED항목이 없습니다 (포크 "검토 비활성화됨" 봇 코멘트는 검토가 아님).
| 카테고리 | 조건 | 제공된 조치 |
|---|---|---|
| 병합 가능한 봇 PR | 허용 목록에 있는 봇 + 준비 완료 + 초안 아님 | 증분, 순서대로 병합 제공 |
| 승인 및 준비 완료 | 유지보수자 승인 + 준비 완료 + 초안 아님 | 병합 프롬프트 |
| 검토되지 않음 | 봇 아님 + 작성자만 검토함(또는 검토 없음) + 초안 아님 | 리뷰 부에이전트 생성 제공 |
| 작업 필요 | 초안, 하드 CI 실패, 보류 중인 CI, 충돌, 뒤쳐짐, 변경 요청, 또는 준비되지 않은 봇 PR | 보고만 — 조치 제공 안 함 |
("준비 완료"는 이미 CI 비차단을 포함합니다. 리뷰 부에이전트는 사람이 작성한 PR용입니다 — 준비되지 않은 봇의 의존성 업데이트는 작업 필요로 보고되며 검토되지 않습니다.)
분류된 PR을 제시하고 AskUserQuestion을 통해 적용 가능한 조치를 제공합니다.
증분, 순서대로 병합 (봇 PR 및 승인된 준비 PR): 먼저 병합 세트와 병합 방법을 확인한 다음 (병합은 되돌릴 수 없음), 하나씩, 가장 오래된 것부터 병합합니다. 저장소가 허용하는 방법을 선택하고 허용하지 않으면 실패 종료합니다:
gh repo view "$REPO" --json mergeCommitAllowed,squashMergeAllowed,rebaseMergeAllowed # positional repo, not -R
각 PR에 대해 순서대로 병합 직전에 다시 확인합니다 (각 병합 후 상태가 변경됨 — 병합된 PR은 다음 PR을 BEHIND, 충돌 또는 재계산 상태로 만들 수 있음), 동기식으로 병합한 다음, 진행하기 전에 병합되었는지 확인합니다:
gh pr view <N> -R "$REPO" --json isDraft,reviewDecision,mergeable,mergeStateStatus,statusCheckRollup
# 아직 준비된 경우에만 진행: 초안 아님, mergeable == MERGEABLE, mergeStateStatus == CLEAN, CI 차단하지 않음
gh pr merge <N> -R "$REPO" --<method> # 절대 --auto 사용하지 않음, 절대 --admin 사용하지 않음
gh pr view <N> -R "$REPO" --json state # 다음 PR으로 이동하기 전에 "MERGED"를 기대함
PR이 더 이상 준비되지 않은 경우( mergeable == "UNKNOWN" 포함), 병합이 이루어지지 않은 경우, 또는 필수 검사가 통과하지 않은 경우 중단하고 보고합니다 — 절대 강제, --admin, --auto를 사용하거나 검사를 건너뛰지 않습니다. 봇 PR의 경우 의존성 및 버전 점프(예: 주요 업데이트)를 게이트에 표시하여 사용자가 컨텍스트를 가지고 결정할 수 있도록 합니다.
리뷰 부에이전트 (검토되지 않은 PR): 사용자가 동의하면, PR당 하나의 Agent를 생성합니다 — 모두 단일 어시스턴트 메시지로 병렬로 실행됩니다. 각 부에이전트는 하나의 PR의 diff를 검토하고 구조화된 리뷰를 반환합니다. 각 리뷰를 작업 디렉토리의 github-pr-<number>-review.md에 씁니다 (해당 PR의 이전 파일을 덮어씀). 리뷰는 읽기 전용이며 GitHub에 게시되지 않습니다. 부에이전트 프롬프트, 검토 기준 및 파일 형식은 references/reviewing-prs.md를 참조하십시오.
병합 후 다시 가져오기 병합된 PR 목록(1단계 --state merged 쿼리)을 수행하여 이슈 단계에서 해당 병합으로 해결된 이슈를 감지할 수 있도록 합니다.
3단계: 각 오픈 이슈 분류
모든 오픈 이슈를 정확히 하나의 버킷으로 정렬합니다.
버킷 A — 이미 해결됨 (작업이 적용되었지만 이슈가 열려 있음). 구체적인 증거 필요; 단일 약한 신호보다 교차 검증을 선호합니다:
- 병합된 PR이
closingIssuesReferences에 이슈를 나열하거나, 제목/본문이 종료 키워드와 함께#N을 참조합니다. (가장 강력함.) - 기본 브랜치의 커밋이 종료 키워드와 함께
#N을 참조합니다. - 이슈가 요청하는 동작이 현재 코드에 명백히 존재 — Grep/Read로 관련 코드를 읽어 확인하고 가정하지 마십시오. 다른 또는 부분적인 구현은 이슈를 해결하지 않습니다.
→ 제안된 쓰기: 해결 PR/커밋을 명명하는 코멘트와 함께 이슈를 종료합니다.
gh issue close <N> -R "$REPO" \
-c "해결됨: #<PR> (<간단한 이유>). 변경 사항이 $default_branch에 있으므로 종료합니다."
버킷 B — 보류 중인 PR이 해결할 예정 (오픈 PR이 이슈를 해결함).
어느 방향으로든 감지: 오픈 PR의 closingIssuesReferences에 이슈가 포함됨; 이슈 본문/코멘트가 PR을 연결함; 또는 오픈 PR이 이슈가 설명하는 것과 동일한 것을 명확히 수정함. 목표는 이슈와 PR이 서로 참조하도록 하는 것입니다 — 실제 격차만 채우고 비파괴적 쓰기를 선호합니다:
- 어느 방향으로도 참조 없음 → 부족한 쪽에 포인터 코멘트(비파괴적)를 게시합니다.
#<PR>/#<N>을 언급하면 GitHub가 다른 쪽의 타임라인에 미러링하는 상호 참조가 생성됩니다.gh issue comment <N> -R "$REPO" -b "#<PR>에서 수정이 진행 중입니다." - 적어도 한 방향으로 이미 연결됨 → "이미 연결됨 — 조치 없음"으로 기록.
사용자가 병합 시 PR이 이슈를 자동 종료하기를 명시적으로 원하지 않는 한 PR 본문을 편집하지 마십시오. 코멘트는 참조를 설정하지만 자동 종료를 트리거하지 않습니다 — 오직 PR 본문의 종료 키워드나 커밋 메시지만 자동 종료를 트리거합니다. 사용자가 동의하는 경우 설명을 덮어쓰지 마십시오: 편집 직전에 본문을 다시 가져와 stdin을 통해 전달하여 신뢰할 수 없는 PR 텍스트가 셸 보간 문자열을 통과하지 않도록 합니다:
body=$(gh pr view <PR> -R "$REPO" --json body --jq .body)
printf '%s\n\nCloses #%s\n' "$body" "<N>" | gh pr edit <PR> -R "$REPO" --body-file -
이미 존재하는 링크를 복제하지 마십시오.
버킷 C — 미해결 (해결책 없음, 보류 중인 PR 없음). 로컬로만 우선순위와 변경 규모 추정치를 할당합니다 (다음 섹션).
4단계: 미해결 이슈 점수 매기기 (로컬 전용)
우선순위 — Critical / High / Medium / Low. 고려 사항:
- 영향 / 심각도 — 보안, 데이터 손실, 크래시 또는 정확성 버그가 개선 사항보다 높습니다. 문서/미용상 최하위. 기존 레이블(
security,bug,crash,regression)은 강력한 신호입니다. - 범위 — 영향을 받는 사용자/워크플로우 수.
- 신호 — 반응(👍), 중복 보고, 지속적인 활동이 있는 연령.
- 긴급성 — 릴리스를 차단하거나, 마감일이 있거나, 활성 회귀가 있습니다.
변경 규모 — 노력 대리자로, 추정된 총 변경 라인(추가 + 삭제, 생성/벤더 파일 제외)에서 Kubernetes/Prow 스타일의 size/* 티셔츠 버킷으로 표현됩니다.
| 라인 범위 | 크기 |
|---|---|
| ≤ 10 | size/XS |
| 11–30 | size/S |
| 31–100 | size/M |
| 101–500 | size/L |
| 501+ | size/XL |
결과를 로컬에만 표시합니다. GitHub에 쓰지 마십시오.
5단계: 결과 제시 및 확인
완료된 모든 작업(병합, 이슈 종료, 코멘트, 리뷰 파일)을 요약합니다. 가장 최근 상호작용 이후 변경된 사항을 명확히 설명합니다.
안전 규칙
- 로컬 우선순위.
Priority: *또는size: *텍스트를 GitHub 이슈나 PR에 절대 쓰지 마십시오. 이는 내부 계획 지원 도구입니다. - 강제 없음. PR 검사 또는 승인을 건너뛰기 위해
--admin,--auto, 또는 플래그를 사용하지 마십시오. - 히스토리 덮어쓰지 않음.
gh pr edit --body-file은 신중하게 사용하십시오 — 템플릿이나 봇의 자리 표시자를 덮어쓰지 않도록 원래 본문을 포함하고 추가만 하십시오. - 의심스러운 봇 PR. 허용 목록에 없는 봇 작성자는 병합 대상으로 제공되지 않습니다. 의심스러운 봇 PR을 보고하되 자동화하지 마십시오.
- 멱등성. 이 스킬을 다시 실행해도 이전에 병합되거나 종료된 항목이 다시 병합되거나 다시 종료되지 않아야 합니다.
- 원격만. 이 스킬은 GitHub 호스팅 저장소에서만 작동합니다.