diff-review
diff / staged / 최근 변경 / PR 등 "변경 집합"을 대상으로 하는 read-only 코드 리뷰. 비용을 요청·리스크에 비례시켜, 기본은 저비용 패스(inline 직독 + 위험 주장만 스크립트·타입체크·테스트로 검증)로 돌리고, 다축 fan-out은 release gate·보안 정밀처럼 명시 요청 시에만 승격한다. 근거(file:line)·반
SKILL.mdskills/diff-review/SKILL.md
GitHubDiff Review
변경 집합(diff·staged·최근 커밋·PR)을 read-only로 리뷰해 근거·우선순위를 갖춘 발견 목록을 만든다. 코드베이스 전반이 아니라 바뀐 것과 그 파급 범위를 본다.
모드 선언
이 스킬이 활성되면 아래 모드로 전환한다:
- read-only 리뷰다. Edit/Write 금지. 수정 지시가 오면
self-feedback-loop로 넘긴다. - 비용을 요청·리스크에 비례시킨다. 기본은 L1(저비용). fan-out(L3)은 자동이 아니다 — 사용자 명시 요청이 있을 때만 올리고 그 이유를 밝힌다. 이게 이 스킬의 존재 이유다.
- 근거 없는 지적을 만들지 않는다. 모든 발견은
file:line으로 실제 코드를 가리킨다. 확인 안 한 일반론은 발견이 아니다. - 오탐보다 신뢰가 먼저다. 그럴듯하지만 틀린 발견 하나가 리뷰 전체의 신뢰를 깎는다. Zero findings는 정당한 결과다.
- 취향이 아니라 비용을 본다. 스타일·린터가 잡을 것 대신 결함·리스크·유지보수 비용에 집중한다.
비용 사다리
기본은 L1. 승격 규칙은 표 아래에 있다 — 특히 fan-out(L3)은 명시 요청 없이는 절대 가지 않는다.
| 레벨 | 방식 | 언제 |
|---|---|---|
| L1 (기본) | inline 직독 + 위험·불확실 주장만 repro 스크립트·타입체크·테스트로 검증. subagent 안 씀. | 수백~1,200줄 규모 diff. 대부분의 커밋·PR 리뷰. |
| L2 | 단일 confidence-filtered 리뷰어 1회(fan-out 없음). | diff가 커서 한 컨텍스트에 안 들어오거나, 격리된 시선이 필요할 때. |
| L3 | 다축 fan-out finder + per-finding verifier. | release promote 직전·보안 민감 변경에서 명시 요청 시에만. |
승격은 두 경로로만 일어나고, 레벨을 섞지 않는다:
- 위험 표면이 큰 diff(신뢰 경계·인증·결제·마이그레이션)는 요청이 없어도 L2까지만 자동 승격한다 — 위험한 변경은 격리된 한 번의 정독을 받을 값어치가 있고, L2는 여전히 저비용이다.
- L3(fan-out)은 사용자 명시 요청("release 전 감사", "보안 정밀 리뷰", "xhigh/max로", "빠짐없이")에서만. diff 내용만으로 자동 L3에 가지 않는다.
승격하면 "Lx로 올림 — 이유"를 한 줄로 밝힌다.
L3의 finder×verifier fan-out은 recall을 위해 비싸다. 한계효용이 빠르게 떨어지므로(중복 발견), 평시 리뷰에 기본값으로 쓰지 않는다.
진행 방식 (L1 기준)
1. 변경 집합 확정
무엇을 리뷰하는지 결정적으로 고정한다. 지시가 없으면 unstaged → staged → 최근 커밋 순으로 대상을 추정하되, 사용자에게 대상 범위를 한 줄로 확인받는다.
git diff,git diff --staged,git diff <base>...HEAD,gh pr diff <n>중 대상에 맞는 것.- diff 크기(파일 수·라인 수)를 먼저 재서 L1으로 충분한지 판단한다.
2. 파급 범위까지 읽기
바뀐 라인만 보지 말고 변경이 건드리는 계약을 본다 — 하지만 레포 전체를 읽지는 않는다.
- 변경된 함수·타입의 호출부, 변경이 깨뜨릴 수 있는 불변식, 삭제·시그니처 변경의 하위 영향.
- 새 값이 흘러 들어가는 경로(입력 검증·경계·null)와 나가는 경로(에러 전파·리소스 정리).
- diff가 스택을 감지하게 하면 관련 비파괴 도구(타입체커·린터)를 돌려 근거를 확보하고, 도구가 이미 잡는 것은 발견에서 뺀다.
3. 위험 주장만 실행 검증
발견 후보 중 런타임 동작에 대한 주장(이 조건에서 터진다·이 값이 새어 나간다)은 말로 단정하지 말고 최소 repro로 확인한다.
- 작은 repro 스크립트·단위 테스트·
node -e·타입체커 한 번이 per-finding verifier agent보다 몇 배 싸고 결정적이다. - run/repro로만 확정되는 발견은 검증 전에는
미검증을 달고 단정하지 않는다.
4. 반증 (Verify)
발견을 목록에 올리기 전에 각 발견을 반증하려 시도한다. 이 단계가 오탐을 거른다.
- Pre-Report Gate: file:line 인용 가능? 구체적 실패 모드 명명 가능? 주변 컨텍스트 읽었나? 심각도 방어 가능? — 하나라도 아니오면 강등/삭제.
- Quote-the-line: High/Critical은 verbatim 근거 라인 인용 필수. 못 하면 Medium 이하로 강등.
- 실제 결함 vs 의도된 설계: "버그인가, 이 변경의 의도적 선택인가"를 구분한다.
- 근본 원인에 앵커: 나쁜 값이 유입되는 origin에
file:line을 단다. 증상 위치에 달면 위임 시 증상만 고쳐진다. - 저확신 Critical은 버리지 않는다:
추정으로 남긴다(fail-closed). - L3로 승격했을 때만 살아남은 High+ 발견을 per-finding 독립 verifier로 검증한다(반증 프레이밍).
깊은 다축 규율(반증 4문항 전체·오탐 블랙리스트·심각도 앵커·보안 룰셋)이 더 필요하면 codebase-review의 references/를 참조한다 — 같은 규율을 공유한다.
심각도 & 확신도
| Severity | 기준 |
|---|---|
| Critical | 런타임 에러, 데이터 손실, 보안 결함 |
| High | 기능 오동작, 계약 불일치, 심각한 구조 결함 |
| Medium | edge case 누락, 유지보수 비용 큰 복잡도 |
| Low | docs 불일치, 사소한 불일관성 |
앵커: 외부 악용 가능 보안=항상 High+, 핫패스 성능=최소 Medium, cosmetic=Low 상한.
확신도는 심각도와 분리해 low/med/high로 매기고, 확신도 낮음은 보고하지 않는다(저확신 Critical은 추정으로 예외).
출력 포맷
요약 헤더 — 리뷰 대상(범위·diff 규모)·실행 레벨(L1/L2/L3, 승격 시 이유) + 통계 한 줄:
대상 <범위> / 레벨 Lx / 발견 Critical a · High b · Medium c · Low d
이어서 심각도별 발견 목록(안정 # 번호):
#2 [High · conf:high] 견고성 — path/to/file.ext:42
근거: `const data = await fetch(url).catch(() => [])`
문제: fetch 실패를 빈 배열로 삼켜 하위 로직이 오작동.
개선: 에러 전파 또는 명시적 fallback. | effort: easy | autofix: manual
각 발견에 conf, effort(trivial/easy/medium/hard), autofix(auto/manual/advisory)를 달아 위임 우선순위 신호로 쓴다.
Coverage: 안 본 영역, 강등·제외한 발견 수, 잔여 리스크, 미검증 항목을 명시한다.
개선으로 잇기
수정 지시("상위 N개 고쳐"·특정 발견 선택)가 오면 직접 고치지 말고 self-feedback-loop로 넘긴다.
선택된 발견을 안정 # 번호 + severity + file:line + 개선 방향으로 전달하고 이를 fix 범위로 명시한다.
작은 수정 한두 건이라 루프가 과하면 그 사실을 밝히고 이 세션에서 바로 고쳐도 된다 — 단 수정 후 관련 빌드·테스트로 검증하고, 검증 전에는 완료라고 하지 않는다.
인접 스킬과의 경계
- codebase-review — 코드베이스 전반 다축 리뷰. 변경 집합이 아니라 프로젝트 전체가 대상일 때. 깊은 반증 규율의 references 소유처.
- self-feedback-loop — plan 기준 review-fix-verify-commit. 이 스킬의 수정 위임처.
- test-code-guide — 테스트 스위트 심층 리뷰·작성 규범. 테스트 축 발견을 깊게 팔 때.
- 내장 code-review / CodeRabbit — L3 게이트에서 명시 호출하거나 사용자가 그 도구를 지정할 때.
Gotchas
- 자동 fan-out 유혹: "빠짐없이 보려면" 하고 바로 다축 fan-out으로 가면 토큰이 세션 한도를 친다. 발견 품질의 대부분은 L1 직독 + 위험 주장 실행 검증에서 나온다. L3 fan-out은 사용자 명시 요청이 있을 때만 — 위험한 diff라도 자동으로는 L2까지다.
- 바뀐 라인만 보기: diff의 추가/삭제 라인만 읽고 호출부·불변식을 안 보면 계약 위반을 놓친다. 반대로 레포 전체를 읽으면 컨텍스트가 고갈된다 — 파급 범위까지만.
- 주장 단정: "이 경우 터진다"를 repro 없이 High로 올리면 오탐이 된다. 런타임 주장은 실행으로 확인하거나
미검증을 단다. - 트리거 오인: "리뷰하고 고쳐"(수정 의도)는 self-feedback-loop, "전체 코드베이스 리뷰"는 codebase-review. 이 스킬은 변경 집합 read-only 리뷰만.
- 컨벤션 오판: 변경이 따르는 프로젝트의 의도적 선택을 "결함"으로 지적한다. 기존 스타일이 표준인지 결함인지 먼저 판단한다.
- 부재 증명을
rg기본값으로 하지 않는다:rg는 hidden 경로를 건너뛴다. "다른 호출부 없음", "삭제해도 안전" 판정이.github/workflow, CI 설정, dotfile 속 소비자를 빠뜨린 목록 위에 서게 된다. 결과가 그럴듯해서 누락이 안 보인다. 참조 조사는rg --hidden에 대상 디렉토리를 명시해서 하고, 삭제·은퇴를 승인하는 finding은 그 결과만 근거로 쓴다.