--- name: pr-description description: > Pull Request 본문을 쓰거나 이미 올린 PR의 본문을 고칠 때 로드한다. 바꾼 파일을 나열하는 대신 해결하려는 문제(why), 머지 후 보장되는 동작(intent), 머지해도 되는지 판정하는 기준(acceptance)으로 본문을 구성한다. 한국어 문장이 GitHub에서 취소선·기울임으로 깨지지 않는지 확인하는 절차까지 포함한다. 코드 자체를 보는 일은 code-review가, 문장의 톤은 engineering-writing이 맡는다. compatibility: > devops 플러그인의 github MCP 서버가 연결돼 있으면 저장소 관례와 PR 상태를 직접 읽는다. 없으면 사용자가 준 정보로 본문 초안만 만든다. --- # 의도로 쓰는 PR 본문 리뷰어는 diff를 읽기 전에 **이 PR이 무엇을 책임지는지** 알아야 한다. 바뀐 것만 나열한 본문은 리뷰어가 의도를 직접 재구성하게 만들어서, 읽는 비용을 늘리고 의도와 구현이 어긋난 경우를 놓치게 한다. ## 리뷰어가 막는 이유는 둘뿐이다 1. 이 변경이 **의도한 결과를 달성하지 못하는가.** 2. 이 변경이 **보안·데이터·가용성 리스크를 새로 들이는가.** 나머지 — 스타일, 네이밍, 관용구, 패턴 — 는 linter와 자동 리뷰에 맡긴다. 본문은 이 두 질문에 답할 재료를 리뷰어 앞에 미리 깔아 두는 문서다. | 절 | 답하는 질문 | |---|---| | 1. 해결하려는 문제 | 질문 1의 전제 — 달성해야 할 결과가 무엇인지 | | 2. 보장하려는 동작 | **질문 1** — 결과를 달성하는지 판정할 기준 | | 4. 알려진 위험 | **질문 2** — 새 리스크가 들어오는지 판정할 재료 | | 5. 머지 가능 기준 | 두 질문이 이미 검증됐다는 증거 | 여기서 실천 규칙이 나온다. **두 질문 어디에도 답하지 않는 내용은 본문에 쓰지 않는다.** 바뀐 파일 목록, 리팩터링 방식 설명, 함수 시그니처 변경이 쓸모없는 이유가 이것이다. 리뷰어가 판단에 쓸 수 없으면서 읽는 시간만 가져간다. ## 제목 저장소 관례를 먼저 본다. github MCP가 연결돼 있으면 최근 머지된 PR 제목을 스무 개쯤 읽고(`list_pull_requests`, state는 닫힌 것) 같은 형태로 맞춘다. 연결돼 있지 않으면 사용자에게 최근 PR 제목 몇 개를 물어보고, 그것도 없으면 관례 없이 쓴 뒤 그 사실을 밝힌다. 연결할 티켓이 있으면 관례에 맞는 자리에 번호를 넣는다. 보통 제목 맨 앞 `[TICKET-1234] 제목` 형태다. 트래커가 제목의 번호를 읽어 PR과 이슈를 자동으로 잇는다. 번호를 모를 때는 지어내지 않는다. 다만 묻는 것이 작업을 막는 게이트는 아니다. 1. 대화·브랜치 이름·커밋 메시지에 이미 번호가 있으면 그것을 쓰고 묻지 않는다. 2. 없으면 올리기 직전에 **한 번만** 묻는다. 3. 없다고 하면 되묻지 않고 번호 없이 올린다. "관련 스펙"에 "해당 티켓 없음 — 운영 중 발견한 단발 수정"처럼 한 줄 남긴다. 4. 같은 세션에서 이미 "없다"는 답을 받았으면 다음 PR에서 다시 묻지 않는다. ## 여섯 개 절 이 순서로 쓴다. 절을 임의로 빼지 않는다. 해당 사항이 정말 없으면 "해당 없음"과 이유를 한 줄로 적는다. 빈 절은 리뷰어에게 "빠뜨린 건가"를 의심하게 한다. ### 1. 해결하려는 문제 (why) - 어떤 사용자·시스템·팀이 어떤 영향을 받고 있었는지. - 이 PR이 그 문제를 어떤 방식으로 해결하는지 한 문장. 구현 디테일이 아니라 책임 범위다. - 관련 사고나 티켓이 있으면 링크한다. 링크만 두고 "자세한 건 거기 참고"로 끝내지 않는다. 리뷰어가 떠나지 않아도 맥락을 잡도록 핵심 한두 문장을 옮겨 적는다. ### 2. 보장하려는 동작 (intent) 머지 후 무엇이 보장되어야 하는지를 **밖에서 관측 가능한 동작**으로 적는다. - 좋음: "한 클러스터의 실패가 다른 클러스터의 정리를 막지 않는다." - 나쁨: "반복문 안에 continue 추가." — 구현 디테일이라 판정 기준이 못 된다. 세 개에서 일곱 개가 적당하다. 더 많으면 PR 범위가 너무 크다는 신호다. ### 3. 관련 스펙 (references) **이 절의 1차 독자는 리뷰어가 아니라 나중에 blame으로 이 코드를 찾아올 사람이다.** 코드를 읽다 맥락이 궁금해지면 그 줄의 blame에서 PR로 온다. 그런데 결정의 히스토리는 대개 PR이 아니라 기술 명세에 있다. PR은 코드에서 결정 히스토리로 가는 유일한 징검다리라, 여기 링크가 없으면 그 경로가 끊긴다. 그래서 스펙·티켓·요구사항 링크는 선택이 아니다. 리뷰어에게 당장 필요 없어 보여도 남긴다. 각 링크에 "왜 관련 있는지" 한 줄을 붙인다. URL만 던지지 않는다. ### 4. 알려진 위험 (side effect) 리뷰어가 "이거 괜찮나"를 묻기 전에 먼저 짚는다. - 밖에서 보이는 동작 변화 (알림 빈도, API 응답 형태, 로그 볼륨) - 마이그레이션과 하위 호환 - 성능 영향 - 범위 한정 — "이 PR은 여기까지, 나머지는 별도 PR" - 영향이 없으면 없다고 적는다. "기본 동작 영향 없음 — 새 플래그 기본값이 false" ### 5. 머지 가능 기준 (acceptance) 체크박스로 쓰고, **머지 전에 판정 가능한 항목만** 넣는다. - 자동 검증(빌드·테스트·린트) 중 이미 통과한 것은 `[x]`로 둔다. - 사람이 머지 전에 확인하는 것(스테이징 배포 확인, 스키마 diff 검토)도 여기 넣는다. 이 절의 체크박스는 **게이트다.** 머지 시점에 `[ ]`가 남아 있으면 "아직 머지하면 안 됨"을 뜻해야 한다. 머지 후에만 확인되는 항목을 섞으면 게이트 신호가 흐려진다. ### 6. 머지 후 확인 (non-blocking) 배포 반영, 다음 배치 실행처럼 머지 이후에나 관측되는 항목. 헤더의 `(non-blocking)`이 게이트와 구분하는 표시다. **각 항목에 언제 볼 수 있는지 적는다.** 관측 시점이 없으면 그대로 방치된다. 하루 넘게 걸리거나 담당자가 바뀔 확인은 PR이 아니라 티켓으로 옮긴다. PR은 머지되는 순간 아무도 다시 열지 않는 문서라, 여기 체크박스는 체크되지 않은 채 고아로 남는다. 이 경우 PR에는 티켓 링크만 남긴다. ## 예시 나쁜 본문 — 바꾼 것만 나열했다. ```markdown ## Summary - cleaner.go의 panic을 log + continue로 변경 - ClusterFailure 구조체 추가 - sendAlert 메서드 추가 ``` 리뷰어가 "왜 이걸 하지", "이게 무엇을 보장하지"를 직접 재구성해야 한다. 좋은 본문 — 문제와 의도가 먼저 온다. ```markdown ## 해결하려는 문제 (why) 지난주부터 정리 배치가 매일 panic으로 죽으면서 (a) 인덱스 정리가 멈춰 디스크가 쌓이고 (b) 결과 알림이 오지 않아 인지가 며칠 늦었다. 근본 원인은 별도 이슈에서 고치는 중이고, 이 PR은 같은 실패가 다시 나도 (a)(b)가 일어나지 않게 만드는 회복력 변경이다. ## 보장하려는 동작 (intent) 1. 한 클러스터의 실패가 다른 클러스터의 정리를 막지 않는다. 2. 클러스터별 결과가 각각 알림 스레드에 보고된다. 3. 실패가 한 건이라도 있으면 담당자가 즉시 인지한다. 4. CI dry-run에서 위 동작을 사전 검증할 수 있다. ## 관련 스펙 (references) - 근본 원인 이슈: (링크) — 이 PR이 우회하는 대상 - 동반 변경: (링크) — 알림 포맷을 맞춘 상대 저장소 PR ## 알려진 위험 (side effect) - 알림 메시지 수가 1건에서 6건으로 늘어난다. 가시성을 우선한 판단이다. - 기본 동작 영향 없음 — 새 플래그 기본값이 false다. ## 머지 가능 기준 (acceptance) - [x] build·vet·test 통과 - [x] 스테이징 dry-run에서 panic 0건 ## 머지 후 확인 (non-blocking) - [ ] 다음 배치에서 클러스터 6개 스레드 응답 도착 (내일 09:00) ``` 머지 가능 기준이 전부 `[x]`라 머지해도 된다는 것이 한눈에 보이고, 아직 못 본 것은 아래로 내려가 게이트 신호를 흐리지 않는다. ## 머지 가능 기준을 채우는 법 5번 절의 체크박스는 **확인한 것만** `[x]`다. 이 런타임은 명령을 실행하지 못하니, 통과 여부는 실행이 아니라 관측으로 채운다. 1. `.github/workflows/*.yml` 같은 CI 정의를 읽고 무엇이 검사되는지 파악한다 (github MCP의 `get_file_contents`, 없으면 사용자에게 요청). 그것이 이 PR의 자동 검증 항목 목록이다. 2. PR이 이미 올라가 있으면 체크 상태를 읽어 통과한 것만 `[x]`로 표시한다. 3. 아직 올리기 전이면 자동 검증 항목을 전부 `[ ]`로 두고, 무엇을 돌려야 하는지 사용자가 바로 실행할 수 있는 명령으로 적는다. 4. **돌리지 않은 검사를 통과했다고 쓰지 않는다.** 사용자가 "테스트 통과했다"고 말해 준 항목만 `[x]`로 옮기고, 근거가 사용자 진술이면 그대로 둔다. CI를 린트 돌려주는 도구로 쓰지 않는다. 실패하고 고치고 다시 푸시하는 왕복은 리뷰어 알림만 늘리고 PR의 첫인상을 망친다 — 사용자에게도 그 점을 한 줄로 알린다. ## 마크다운 렌더링 확인 본문을 넘기기 전에 GitHub Flavored Markdown으로 어떻게 보일지 확인한다. 멀쩡해 보이던 한국어 문장이 취소선으로 깨져 나오면, 의도가 전달되기 전에 신뢰를 잃는다. **가장 흔한 사고는 물결표다.** 한국어에서 범위를 `6~15분`처럼 쓰는데 GitHub은 `~text~`를 취소선으로 렌더링한다. 한 줄 안에 물결표가 둘 이상이면 그 사이가 통째로 지워진 것처럼 보인다. 개수가 홀수면 우연히 멀쩡해 보이다가 나중에 한 줄만 고쳐도 바로 깨진다. 백슬래시로 이스케이프한다 — `6\~15분`. 범위 표기를 굳이 유지할 필요가 없으면 `6-15분`, `6분에서 15분`도 괜찮다. 함께 볼 것: - 백틱 밖의 언더스코어 — `user_event_log`를 그냥 쓰면 가운데가 기울임이 된다. - 백틱 밖의 `*`는 강조로, `<...>`는 HTML 태그로 먹힌다. - 표 셀 안의 `|`는 열을 밀어낸다. `\|`로 이스케이프한다. ## 문장과 안전 - 본문의 톤은 `engineering-writing`을 따른다. 이 스킬은 무엇을 어느 절에 쓸지만 정한다. - 작성 언어는 저장소 관례를 따른다. 관례가 없으면 리뷰어의 언어로 쓴다. 코드 식별자와 외부 시스템 이름은 원문 그대로 둔다. - **사용자의 명시적 요청 없이 PR을 만들거나 고치지 않는다.** 기본 산출물은 복사해서 쓸 수 있는 본문 초안이다. 사용자가 올려 달라고 하면 그때 github MCP로 만들고 (`create_pull_request`), 이미 올라간 PR의 본문 교체도 요청을 받은 뒤에 한다. - 코드 자체에 대한 지적은 여기 쓰지 않는다. 리뷰가 필요하면 `code-review`로 넘어간다.