write-gate · diff
git:20260817.f62ba51 to git:20260911.97e80e4
45 added, 336 removed. Audit A to A.
---
name: write-gate
description: >-
- Pre-write and review gates for code changes. Pre-write: before
- creating any new function, helper, type, or file — 만들어줘, 추가해줘,
- 구현해줘, 새 파일, 리팩토링, new helper/component. Post-write: after
- finishing a change — 다 했어, 리뷰해줘, 검토, review my change.
- Prevents duplicate reimplementation and enforces the review checklist
- including run-verification (scaled to change size/runtime surface — no
- full verification for small mechanical changes) and doc sync.
- user-invocable: false # 사용자 표면은 /hi-vibe:* 명령 10개다. 스킬까지 슬래시 메뉴에 나오면 16개가 되어 "외울 게 적다"는 약속이 깨진다. Claude의 자동 호출은 그대로 유지된다.
+ Use before creating or refactoring functions, helpers, types, components,
+ or files, and after finishing code changes that need review. Triggers on
+ 만들어줘, 추가해줘, 구현해줘, 새 파일, 리팩토링, 다 했어, 리뷰해줘,
+ 검토, and review my change.
+ user-invocable: false
---
# write-gate
- > **답변 언어**: 이 지침은 한국어로 쓰였지만, **출력은 항상 사용자가 대화에서 쓰는 언어**를 따른다 (한국어→한국어, 영어→영어). 기존 문서·코드에 언어가 있으면 그쪽을 우선한다.
-
- ## 먼저: 안전벨트가 매여 있는지 (두 모드 공통, 세션당 한 번)
-
- **훅은 조용히 실패한다**(fail-open) — 망가져도 에러조차 안 뜬다. 그래서
- "훅이 죽었나"를 훅으로는 알 수 없다(자기가 안 도니까). 확인할 수 있는 건
- **훅과 무관하게 도는 이 스킬 층뿐**이다. 이 스킬은 사용자 말("만들어줘",
- "다 했어")에 걸리므로 훅이 전멸해도 살아 있다.
+ 출력은 사용자의 대화 언어를 따른다. 이 파일에는 모든 모드가 함께 쓰는 판단과
+ 분기만 둔다. 긴 체크리스트·사례·보고 형식은 필요한 모드에서만 references를 읽는다.
- 이 스킬이 이번 세션에서 처음 걸릴 때 한 번만:
+ ## 세션당 한 번: 훅 생존 확인
`python3 "${CLAUDE_PLUGIN_ROOT}/scripts/doctor.py" --root <repo> --quick`
- JSON `state` 하나로 갈린다. **`alive`면 아무 말도 하지 마라** — 정상을
- 보고하는 건 잔소리다.
-
- - **`alive`** → 침묵. 그대로 아래 작업을 진행한다.
- - **`stale` / `never-ran`** → 훅이 이 세션에서 안 돌았다. 한 줄로 알린다:
- "hi-vibe 훅이 이 세션에서 안 돌고 있습니다(마지막 기록 N시간 전). 방금
- 플러그인을 업데이트했다면 Claude Code를 다시 시작하면 되고, 아니면
- `/hi-vibe:doctor`로 확인하세요." **막지는 마라** — 훅 없이도 이 스킬은
- 동작하므로 하던 일은 그대로 진행한다.
- - **`not-initialized`** → 이 폴더는 hi-vibe가 꺼져 있다. 한 번만 알린다:
- "이 폴더는 hi-vibe가 꺼져 있습니다(자동 감지·기록 없음). 쓰시려면
- `/hi-vibe:init`, 안 쓰실 거면 그렇다고 말씀만 주세요." 사용자가 "안
- 쓴다"고 하면 `.hi-vibe/optout` 파일을 만들어라(`mkdir -p .hi-vibe &&
- touch .hi-vibe/optout`) — 그러면 훅은 계속 꺼진 채로, 다시 묻지 않는다.
- **여기서 마음대로 init하지 마라.** opt-in은 사용자가 정한다.
- - **`optout`** → 침묵. 이 폴더에서 다시는 묻지 않는다.
-
- **`tracked_env`가 비어 있지 않으면** `state`와 별개로 **반드시 알린다**(세션당
- 한 번). 목록에 있는 `.env` 파일이 Git에 올라가 있다는 뜻이고, **비밀키 검사는
- `.env`를 검사 대상에서 빼므로 이건 훅도 `check`도 못 잡는다.**
-
- > `.env`가 Git에 올라가 있습니다(`<파일들>`). 비밀키 검사는 `.env`를 안 보기
- > 때문에 이건 아무 검사에도 안 걸립니다. `git rm --cached <파일>` 후
- > `.gitignore`에 넣으세요. **이미 push했다면 히스토리에 남아 있으니 그 키는
- > 폐기(rotate)해야 합니다.**
-
- 파일을 열어 보지 마라 — 내용을 읽는 순간 대화창이 유출 통로가 된다. 지워주지도
- 마라(사용자가 결정한다). 알리고 하던 일을 계속한다.
-
- 조회 자체가 실패하면(스크립트 없음·python3 없음) 조용히 넘어간다 — 확인을
- 못 한 것이 작업을 막을 이유는 아니다.
+ - `alive`·`optout`: 말하지 않고 진행한다.
+ - `stale`·`never-ran`: 훅이 돌지 않았다고 한 줄 알리고 작업은 계속한다.
+ - `not-initialized`: 자동 감지·기록을 쓰려면 `/hi-vibe:init`이라고 한 번만
+ 알린다. 마음대로 init하지 않는다. 사용자가 쓰지 않겠다고 하면
+ `.hi-vibe/optout`을 만든다.
+ - `tracked_env`가 있으면 파일명만 알린다. 파일 내용은 열지 않는다. 이미
+ push했다면 키 폐기가 필요하다고 말한다.
+ - 조회가 실패해도 작업을 막지 않는다(fail-open).
## Mode: find (코드 작성 전)
- 새 function/helper/type/파일을 만들기 전에, 순서대로:
-
- 1. **존재 확인**: repo-xray로 그럴듯한 이름 2~3개를 검색한다.
+ 1. 새 이름 후보 2~3개를 repo-xray로 검색하고 스캔 범위를 확인한다.
`python3 "${CLAUDE_PLUGIN_ROOT}/skills/repo-xray/scripts/audit.py" find <name> --root <repo>`
- 결과의 스캔 범위를 인용해 판정한다 (repo-xray 계약).
- 2. **외부 API 근거 확인**: 새 코드가 외부 라이브러리/프레임워크의
- API를 사용하거나 버전에 민감한 부분(파라미터명, 옵션, 반환 형태,
- 설정 키)을 다루면, 기억으로 쓰지 않는다. context7 MCP가 연결되어
- 있으면 `resolve-library-id` → `query-docs`로 최신 문서를 확인하고,
- 없으면 공식 문서를 WebFetch로 확인한다. 둘 다 불가하면 추정임을
- 밝히고 쓴다 (grounded-answers 계약). 프로젝트 내부 코드만 다루는
- 경우 이 단계는 건너뛴다.
- 3. **위치 확인**: 대상 폴더의 `MODULE.md`를 읽는다 — 이 코드가 그
- 폴더의 책임에 맞는가? 안 맞으면 맞는 폴더를 찾거나 사용자에게 묻는다.
- 4. **SSOT**: 공용 유틸/타입/shape은 지정된 공용 위치에 한 번만.
- 로컬 복사본을 만들지 않는다.
- 5. **배치**: 새 파일은 위계(도메인/레이어)에 맞게. 루트에 평탄하게
- 쌓지 않는다. 처음부터 파일을 나눈다 — 한 파일에 몰지 않는다.
- 6. **경계 선언**: 이 코드가 import해도 되는 것 / 이 코드를 import해도
- 되는 곳을 한 줄로 선언하고 시작한다.
-
- 판정은 셋 중 하나: **재사용** (기존 것 그대로) / **확장** (기존 것
- 수정) / **신규** (근거: 스캔 범위 내 없음).
-
- ### 요청 범위 밖을 발견하면 — 고치지 말고 물어라
-
- 코드를 보다 보면 요청과 무관한 문제가 눈에 띈다. 그건 좋은 발견이지만
- **거기서 손대기 시작하면 "한 줄 고쳐줘"가 기능 추가가 된다.** 실제 사례:
- "사이트에 적어줘"라는 요청으로 시작해 봇 기능 신설까지 번졌고, 그 새 기능의
- 버그를 잡느라 작업이 배로 늘었다.
-
- - **요청한 것만 한다.** 밖에서 발견한 건 **한 줄로 보고하고 사용자가 정한다**:
- "사진이 담겼는지 아무도 모르는 상태예요. 지금 고칠까요, 따로 볼까요?"
- - 사용자가 "같이 해줘"라고 하면 그때 한다. **묻는 데 드는 건 한 줄이고,
- 안 물으면 몇 배가 든다.**
- - 예외는 **요청한 것을 하려면 반드시 통과해야 하는 것**뿐이다(그게 막혀서
- 요청 자체가 불가능한 경우). 그땐 왜 필요한지 밝히고 진행한다.
+ 2. 외부 API·라이브러리의 버전 민감한 동작은 공식 문서로 확인한다. 확인할 수
+ 없으면 추정이라고 밝힌다. 프로젝트 내부 코드만 다루면 생략한다.
+ 3. 대상 폴더의 `MODULE.md`가 있으면 책임과 경계를 확인한다.
+ 4. 공용 유틸·타입·shape은 기존 SSOT를 재사용하고 로컬 복사본을 만들지 않는다.
+ 5. 판정은 **재사용 / 확장 / 신규** 중 하나로, 신규라면 검색 범위를 근거로 남긴다.
- 이건 "발견하지 마라"가 아니다. 발견은 하되 **범위를 넓히는 결정은 사용자
- 것**이라는 뜻이다.
+ 요청 밖 문제를 발견하면 한 줄로 보고하고 사용자가 정하게 한다. 요청 자체를
+ 완료하는 데 필수인 수정만 이유를 밝히고 함께 처리한다.
## Mode: review (코드 작성 후)
- **플래그가 없다.** 범위·깊이·병렬 여부는 아래 순서대로 **기계가 준 숫자를
- 보고 여기서 판단한다** — 사용자에게 고르라고 묻지 않는다. 사용자는 무엇을
- 켜야 좋은지 알 수 없고, 물어보는 순간 제일 좋은 기능이 안 켜진 채로 끝난다.
-
- ### 1. 범위 (헬퍼가 코드로 정확히 정한다 — AI가 해시를 세지 않는다)
-
- `python3 "${CLAUDE_PLUGIN_ROOT}/skills/write-gate/scripts/review_scope.py" list --root <repo>`
- → `scope`·`scope_label`(어느 계단에서 잡았나), `to_review`(아직/다시 봐야 할
- 파일), `skipped`(이미 봤고 그 뒤로 안 바뀐 것), `sizes`·`total_changed_lines`·
- `file_count`(규모), `fingerprint`.
-
- - **`deleted`가 비어 있지 않으면** 그 파일들은 열어볼 수 없다. 대신
- **남은 호출부**를 찾아라 — `import`·호출·문자열 경로. AI가 파일을 통째로
- 지우고 부르던 곳을 안 고친 경우가 실제 위험이고, 지운 코드는 리뷰 대상에서
- 빠지기 쉬워 아무도 안 본다. 남은 참조가 있으면 그 자리에서 고친다.
- - **`to_review`가 비었으면**(그리고 `deleted`도 비었으면) "새로 리뷰할 변경이
- 없습니다 (이미 본 것 N개는 그대로)"만 알리고 끝낸다 — 억지로 훑지 않는다.
- - **`scope`가 `uncommitted`가 아니면** 무엇을 보고 있는지 한 줄로 밝힌다:
- "안 커밋한 변경이 없어서 <scope_label>을 봅니다." 커밋·푸시했다고 리뷰가
- 죽지 않게 계단으로 내려가지만, 사용자는 지금 뭘 보는지 알아야 한다.
- - **사용자가 범위를 말했으면**(예 "로그인 쪽만", "3일치", "이 파일만") 그쪽을
- 우선한다. 헬퍼 범위보다 사용자 말이 위다.
- - **git 저장소가 아니면** 범위를 못 구한다 — 그 사실을 알리고, 이번 세션에
- 만진 변경을 사용자와 확인해 아래 체크리스트로 처리한다.
-
- ### 2. 규모가 크면 병렬 (묻지 말고 알리고 진행)
-
- **임계값을 코드에 박지 않는다** — 기계는 `total_changed_lines`·`file_count`만
- 주고, 판단은 여기서 한다. 어림잡아 **파일이 여러 개이면서 수백 줄 이상**이면
- 병렬로 간다(정확한 컷은 상황 판단). 순차는 뒤로 갈수록 얕아지는데, 그걸
- 감수하고 순차를 고를 사용자는 없다.
-
- 병렬로 갈 때는 **고르라고 묻지 말고, 무엇을 왜 하는지와 대가를 한 줄로 알리고
- 그대로 진행한다**:
-
- > 변경이 큽니다 (12파일 1,430줄). 얕아지지 않게 병렬로 봅니다. 토큰을 더 씁니다.
-
- `python3 "${CLAUDE_PLUGIN_ROOT}/skills/write-gate/scripts/review_scope.py" chunk <N> --root <repo>`
- 로 줄 수 균형이 맞은 `buckets`(파일 그룹) N개를 받아, **각 버킷마다 Agent
- 도구로 리뷰어를 하나씩 병렬 소환**한다. 각 리뷰어에게 "이 파일 묶음에 아래
- 체크리스트를 적용하고 ✅/⚠️ 요약을 돌려달라"고 시키고, 결과를 모아 통합
- 보고한다. N은 규모에 맞게(대개 3~6), 버킷 수를 넘기지 않는다. 규모가 작으면
- 이 단계를 건너뛰고 그냥 순차로 간다.
-
- 사용자가 "가볍게 봐줘"(영어로는 "keep it light" / "light review")라고 했으면
- 병렬도 fresh-eyes도 생략하고 체크리스트만 돌린다 — 끄는 건 플래그가 아니라
- 말로 한다. 영문 랜딩이 이 말을 escape hatch로 안내하므로 영어 표현도 받는다.
-
- ### 3. 체크리스트
-
- `to_review`의 각 파일(=기능 덩어리)에 아래를 적용한다(병렬이면 각 버킷
- 안에서). 각 항목을 ✅/⚠️로 보고한다. ⚠️는 이유와 수정 방법을 붙인다.
- 비례 원칙: 이번 변경과 무관한 항목은 "해당 없음" 한 마디로 통과시켜라
- — 형식적 도배도, 통째 생략도 금지. 단 9·10(필수)은 항상 답한다. 파일이
- 많으면 파일별 한두 줄 요약(✅/⚠️)으로 압축하고 ⚠️만 펼치되, 실행 검증·문서
- 동기화(9·10)는 전체에 대해 마지막에 한 번 종합해 답한다.
-
- **자기 점검 루프 (핵심)**: ⚠️는 보고하고 끝내는 게 아니다. 그 자리에서
- 고친 뒤 해당 항목을 다시 검사해 ✅로 만든다 — 통과할 때까지 반복.
- 바로 못 고치는 ⚠️만 남기되, "왜 지금 못 고치는지 + 언제/어떻게 고칠지"를
- 사용자에게 명시한다. 체크리스트는 흠집 목록이 아니라 흠집을 없애는
- 루프다.
-
- **단, 고치기 전에 어느 쪽인지 가른다 (필수):**
-
- - **원래 요구사항을 어긴 결함** → 그 자리에서 고친다. 예: "일부만 저장됐는데
- 전부 성공이라고 표시한다" — 사용자가 요청한 적 없는 거짓 보고이므로 물어볼
- 게 없다.
- - **새 동작·알림·정책이 필요한 것** → **고치지 말고, 발견 사실과 대안을
- 보고하고 확인받는다.** 예: "권한이 없으면 회의당 한 번 안내 메시지를
- 보내자" — 없던 사용자 경험을 새로 만드는 결정이다.
-
- 가르는 기준 한 줄: **이걸 고치면 사용자가 여태 못 보던 화면·메시지·규칙이
- 새로 생기는가?** 생기면 묻는다.
-
- 이 구분이 없으면 리뷰의 자동 수정 루프가 **기능 확장으로 번진다**. 그건 이
- 플러그인이 막으려는 증상("헷갈리는 결정을 사용자에게 묻지 않고 임의 진행")을
- 리뷰가 직접 저지르는 것이고, `fresh-eyes`가 잡으라고 있는 스코프 크립을
- 같은 리뷰가 만들어내는 자기모순이다.
-
- 1. 에러 삼킴을 새로 추가하지 않았는가 (root-cause-first 금지 패턴)
- 2. 중첩 ≤ 3단계 (넘으면 early return / guard clause로 평탄화)
- 3. 크기 상한: 함수 ~50줄, 파일 ~400줄. 파일은 `list`의 `oversized`를 그대로
- 쓴다(눈으로 세지 마라). **`lines - growth`가 400 이하면 이번에 처음 넘긴
- 것 — 그것만 ⚠️로 펼쳐 분리를 제안한다.** 원래 넘어 있던 파일은 한 줄로
- 축약하고(`kis_client.py 686줄, 이번 +26`), `growth`가 음수면(줄인 것)
- 아예 짚지 마라. 같은 경고를 매번 똑같이 펼치면 쌓여서 신호가 아니게
- 된다. **기준 400은 그대로다 — 무르게 하는 게 아니라 반복을 줄이는 것이다.**
- 4. 중복 생성 vs 교체: 기존 코드를 대체했다면 옛 버전을 지웠는가,
- 호출부를 옮겼는가, 죽은 코드/주석 처리 코드가 남지 않았는가
- 5. 안 쓰는 re-export/import 잔재가 없는가
- 6. 순환 의존을 새로 만들지 않았는가 (의심되면 repo-xray scan)
- 7. 숨은 결합을 새로 만들지 않았는가 — 전역 상태 공유, 초기화 순서
- 의존, import 부수효과, 암묵 계약(호출 순서/형태를 문서 없이
- 가정), 한 모듈로의 과도한 fan-in/fan-out 쏠림
- 8. 테스트: 문자열 비교가 아니라 동작을 검증하는가, mock은 외부
- 경계에서만인가, 엣지케이스(빈 값/None/0/경계값/실패 경로)를
- 먼저 다뤘는가, 테스트 통과용 방어코드를 프로덕션에 넣지 않았는가
- 9. **실행 검증 (변경 크기·런타임 표면에 비례)**: 검증 강도는 이번
- 변경이 실제로 만든 위험에 맞춘다 — **작은 변경에 풀 검증을 요구하지
- 않는다.** 세 등급으로 판단한다:
- - **런타임 표면 없음** (문서·주석·순수 포맷, 동작이 그대로인 설정/
- 이름 변경) → 실행 검증 불필요. "런타임 표면 없음"으로 통과하고,
- 앱을 억지로 띄우지 않는다.
- - **작은·국소 변경** (기존에 검증된 패턴을 그대로 복제, script 태그·
- 설정 한 줄 추가, 좁은 범위 수정) → **가벼운 검증으로 충분하다**:
- 구문/임포트 체크(`node --check`·import), 파일이 실제로 서빙/로드
- 되는지 확인 정도. 풀 브라우저 E2E나 여러 조건 렌더는 불필요.
- - **동작을 바꾸는 변경** (로직·API·스키마·새 기능·버그 수정) →
- 실제로 실행해 동작을 관찰한다 — 테스트 실행, 앱 구동, 버그면 원래
- 재현 케이스 재실행. 작성자가 눈으로 읽는 것은 검증이 아니다.
- **UI·CSS·레이아웃·스타일 변경 → 사용자가 검증 루프다 (특례)**:
- 생김새·간격·정렬·색·반응형 같은 시각적 변경은 위 등급과 별개로,
- **에이전트가 브라우저(Playwright 등)를 띄워 자가 검증하지 않는다.**
- 사용자가 앱을 직접 띄워 보고 있으면 화면을 보는 것 자체가 검증이고,
- 에이전트가 딴 브라우저를 또 띄우는 건 이중일이다(레이아웃이 어긋나면
- 사용자가 즉시 본다). 대신: ①변경하고 ②캐시버스팅(`?v=N`)했으면 그
- 사실과 함께 ③"새로고침(⌘⇧R) 후 무엇을 확인하면 되는지"를 한 줄로
- 알려주고 사용자에게 넘긴다. 같은 화면을 여러 상태(모드·화면폭·로딩중)로
- 반복 렌더해 자가 확인하는 것은 금지. CSS 값은 브라우저로 픽셀을 실측해
- 상수로 박지 말고(예: `min-height:529px`는 행 높이가 바뀌면 깨지는 땜빵)
- 콘텐츠 기반(행 수·`rem`·내용에 맞는 예약)으로 견고하게 잡는다. **예외**:
- 사용자가 "브라우저로 확인해줘"라고 명시했거나, 특정 화면폭에서만 깨지는
- 회귀처럼 눈으로 못 잡고 렌더로만 재현되는 경우 → 그때만 최소 렌더.
- **최소 충분 원칙 (과잉검증 금지)**: 한 번의 관찰로 확인되면 멈춘다.
- 같은 것을 여러 조건(화면 폭·브라우저·반복 실행)으로 다시 확인하거나,
- 요청 범위 밖 코드까지 파고들어 검증하지 않는다 — 그건 꼼꼼함이 아니라
- 시간 낭비다(사용자가 답변 하나에 12분을 기다리게 만든다). 해당 등급에
- 필요한 실행을 못 했다면 "실행 검증 안 됨"과 이유를 명시한다 —
- "될 겁니다"는 금지 (grounded-answers 계약).
- 10. **문서 동기화 (필수, 자동)**: 폴더 책임이 바뀌었으면 → 해당
- `MODULE.md`를 같은 턴에 갱신. `CLAUDE.md`는 **코드만 봐서는 모를 것**이
- 바뀌었을 때만 건드린다 (새 제약·새 함정·기록할 결정·새로 만든
- MODULE.md 포인터). 파일을 옮긴 것은 여기 해당하지 않는다 — 구조 변경
- 때마다 CLAUDE.md를 고치게 만들면 결국 거짓말하는 문서가 된다. **실질 변경이면 → 사용자가 `/hi-vibe:log`를 치기를
- 기다리지 말고, 지금 이 자리에서 `CHANGELOG.md`의 `[Unreleased]`에
- 직접 항목을 추가한다** (docs-keeper의 log 모드 규칙을 따른다:
- `date '+%Y-%m-%d %H:%M'`로 실제 시각, Added/Changed/Fixed/Removed로
- 분류, 한 변경당 한 줄). **단 "실질 변경"만** — 동작·API·스키마·의존성·
- 설정 변화. 오타·포맷·주석·동작 동일한 순수 리팩토링은 적지 않는다
- (CHANGELOG를 노이즈로 채우지 않는다). `init`이 CHANGELOG를 만들므로
- 보통은 이미 있다 — 없으면 `docs-keeper`의 템플릿으로 만들고 적는다
- (기록할 게 생겼는데 파일이 없다고 그냥 넘기면 그 변경은 영영 안 남는다).
-
- ### 4. fresh-eyes (기본 켬 — 작을 때만 생략)
-
- 체크리스트를 마치면 **fresh-eyes 에이전트를 Agent 도구로 소환한다.** 이건
- 기본값이지 옵션이 아니다 — 체크리스트가 이 파일 안의 "빠뜨림"을 잡는다면
- fresh-eyes는 **"절반만 끝난 것"**(고친 파일을 가리키는 다른 파일이 안 따라감)
- 과 "판단 착오"(과잉 설계·스코프 크립·더 단순한 길)를 잡고, 그건 작성자가
- 자기 의도를 알고 있어서 혼자서는 못 보는 것이다.
-
- - 전달할 것: ①사용자의 원래 요구사항 한 줄 ②이번에 바꾼 파일 목록.
- - 전달하지 말 것: 설계 이유나 변명 — 에이전트는 깨끗한 눈이어야 한다.
- 작성자의 착각을 공유하지 않는 것이 이 리뷰의 존재 이유다.
- - 사용자에게 말할 땐 **이름을 `fresh-eyes`로 쓴다.** 별명이 여러 개면
- 무엇이 돌았는지 세션 기록에서 못 찾는다 ("남의 눈"으로 바꿔 부르지 마라).
- - 판정이 돌아오면: 요약해 사용자에게 전하되, "재고 권장" 항목마다
- 동의하는지/반박하는지 근거와 함께 명시한다. 기계적으로 전부
- 수용하지도, 전부 방어하지도 않는다.
- - **"확인 필요"는 재고와 섞어 보고하지 마라** — 돌려봐야 아는 것이다.
- 여기서 돌릴 수 있는 것이면 돌려서 결론을 내고, 사용자 환경이라야 알 수
- 있으면(브라우저·실제 장 시간) 무엇을 확인하면 되는지 한 줄로 넘긴다.
-
- **"사용자가 요청 안 했으니 안 부른다"는 오해다.** `사용자가 요청하지 않으면
- 서브에이전트를 부르지 마라` 같은 세션 지시가 있어도, **이 호출은 그 요청
- 안에 이미 들어 있다** — 사용자가 `init`으로 hi-vibe를 켰고, Stop 훅이 매번
- `fresh-eyes까지 수행하라`고 지시하고 있다. 그게 요청이다. 이 판단이
- 세션마다 갈려 **어떤 세션은 하루 종일 체크리스트만 돌았다**(2026-08-07).
-
- **생략하는 경우**: ①변경이 작을 때(오타·주석·포맷·한 줄 설정처럼 판단할
- 설계가 없는 것) ②Agent 호출이 실제로 실패할 때. 둘 다 **조용히 넘기지 말고
- 한 줄로 밝힌다** — "변경이 작아 설계 리뷰는 건너뜀" / "Agent 호출 실패로
- 설계 리뷰 못 함". 무엇이 안 돌았는지 사용자가 알아야 한다. **"막혀 있어서"로
- 뭉뚱그리지 마라** — 안 부른 것과 못 부른 것은 다르고, 사용자가 고칠 수
- 있는지가 갈린다.
-
- ### 5. 완료 기록 (필수)
-
- 실제로 리뷰를 마친 파일들을
- `python3 "${CLAUDE_PLUGIN_ROOT}/skills/write-gate/scripts/review_scope.py" mark <파일들> --root <repo>`
- 로 표시한다 → 다음 리뷰에서 안 바뀐 건 자동으로 건너뛴다. Stop 훅도 이
- 표시를 보고 조용해지므로, **mark를 빠뜨리면 같은 변경으로 리뷰가 다시
- 걸린다.** (⚠️를 못 고치고 남긴 파일은 mark 하지 마라 — 다음에 또 걸려야 한다.)
-
- **표시만으로는 안 풀린다.** 파일 2개 이상을 fresh-eyes 없이 표시하면 Stop 훅이
- 다시 막는다 — 호출 여부를 **대화 기록에서 직접 세기 때문에** 자기신고로는 못
- 넘긴다. 4번을 정말로 생략해야 하는 상황(작은 변경·Agent 호출 실패)이면 그
- 사실을 한 줄로 밝혀라. 같은 파일로 두 번은 안 막는다.
-
- `skipped`가 있었으면 "이미 리뷰됨(변경 없음): N개" 한 줄로 밝힌다.
-
- ### 보고는 짧게 — 한 번 말한 것을 또 말하지 마라
-
- 리뷰가 값을 하려면 사용자가 **읽어야** 한다. 길면 안 읽는다.
-
- - **같은 결함을 두 번 설명하지 마라.** 체크리스트 ⚠️와 마무리 요약에
- 같은 내용을 반복해 적는 일이 실제로 일어난다. **가장 구체적인 자리 한
- 곳에만** 쓰고 나머지는 짧게 가리킨다. **단 `👋` 줄은 여기서 뺀다** —
- 그건 설명이 아니라 **세는 표시**라, 내용이 체크리스트에 이미 있어도
- 아래 세 조건이 맞으면 붙인다.
- - **✅는 나열하지 마라.** "그 외 항목 통과" 한 줄로 묶고 ⚠️만 펼친다.
- 파일이 많아도 마찬가지다.
- - **시간은 묻기 전엔 말하지 말고, 물으면 재서 답하라.** 안 물었는데 붙이는
- 소요 시간 해명은 노이즈다. 하지만 **"왜 이렇게 오래 걸렸냐"고 물으면
- 추측하지 말고 실제 시각을 재서 구간별로 답한다**(git 커밋 시각·명령 로그).
- "리뷰 때문인지 기능 때문인지"는 사용자가 알 자격이 있고, 추측으로 답하는
- 건 grounded-answers 위반이다. **일을 키운 쪽이 자신이면 그렇게 말하라** —
- 리뷰 탓으로 돌리지 않는다.
- - 끝에 **"빠르게 끝내고 싶으면 '가볍게 봐줘'라고 하시면 됩니다"**를 한 번만
- 덧붙인다(세션에 이미 말했으면 생략) — 끄는 법을 모르면 마찰이 불만이 된다.
- **다만 이건 "브레이크가 있다"는 안내지, 깊이를 사용자에게 넘기는 게 아니다.**
- 위 "고르라고 묻지 않는다"는 그대로다 — 이번 리뷰를 어디까지 할지는 여전히
- 네가 정하고, **"다음엔 어떻게 할까요?"로 되묻지 마라.** 실제로 사용자가
- "리뷰가 값어치 있었냐"고 물었을 때 이 문장을 **답 대신** 내놓은 적이 있다.
- 그건 판단을 떠넘긴 것이다 — 값어치 판단은 재서 답하고, 이 안내는 그 뒤에
- 한 줄로만 붙인다.
-
- 평소 리뷰 한 건이면 이 정도가 목표다:
-
- ```text
- 일부 저장을 전체 성공으로 표시하던 문제와, 권한 없는 서버에서 성공 신호가
- 나오지 않는 문제를 발견해 고쳤습니다. 그 외 항목 통과.
- 👋 hi-vibe가 방금 …을 잡아서 고쳤어요 — 체크리스트.
- 실행 검증: 테스트 75개 통과 · 문서 갱신함: CHANGELOG
- ```
-
- ⚠️가 많거나 fresh-eyes가 재고를 권했으면 당연히 길어진다 — **줄이라는 건
- 중복과 변명이지 내용이 아니다.**
-
- ### 마지막 두 줄
+ 리뷰를 시작하면 반드시
+ `references/review-checklist.md`를 읽고 그 절차를 끝까지 따른다. 핵심 순서는:
- 반드시 끝낸다 — 첫 줄은 9번 등급에 맞는 하나로:
- "실행 검증: <무엇을 어떻게 실행해 확인했나>" / "가벼운 검증: <구문·서빙 확인>" / "화면 확인 요청: <⌘⇧R 후 무엇을 볼지>" / "런타임 표면 없음 — <이유>" / "실행 검증 안 됨 — <이유>"
- "문서 갱신함: <목록 (CHANGELOG 자동 기록 포함)>" 또는 "문서 영향 없음 — <이유>"
+ 1. `review_scope.py list`로 리뷰 범위를 기계적으로 구한다.
+ 2. 변경이 크면 줄 수 균형으로 나눠 병렬 리뷰하고, 작으면 순차로 본다.
+ 3. 체크리스트에서 결함을 찾으면 요구사항 안의 결함만 고쳐 재검사한다.
+ 4. 작지 않은 변경은 `fresh-eyes`를 실행해 파일 사이 어긋남과 판단 착오를 본다.
+ 5. 실행 검증과 문서 동기화를 마친 뒤 `review_scope.py mark`로 완료를 표시한다.
+ 6. 결과는 결함과 검증 근거 중심으로 짧게 보고한다.
- ## hi-vibe catch — 반사실적 발견에만 공을 밝힌다
+ 사용자가 “가볍게 봐줘” 또는 “keep it light”라고 하면 병렬과 fresh-eyes를
+ 생략하고 체크리스트만 적용한다. 생략 사실은 한 줄로 밝힌다.
- hi-vibe는 티 안 나게 뒤에서 잡아주는 게 설계라, 잘 작동할수록 자기 공을
- 못 드러낸다. 그래서 이 리뷰가 **사용자가 그냥 지나쳤을 것을 붙잡았을 때만**,
- 보고 맨 끝에 한 줄을 더한다. 세 조건을 **모두** 만족할 때:
- ① hi-vibe(이 스킬/에이전트)가 찾았다 — 사용자·메인 흐름이 자발적으로 짚은 게 아니다
- ② 진짜 결함·판단이다 — 스타일 트집·취향·이미 ✅로 통과한 항목이 아니다
- ③ 이 스킬이 돌기 전엔 레이더 밖이었다 — 안 돌렸으면 놓쳤을 것이다
+ ## 정량 성과가 나온 경우
- 형식(문구는 사용자 언어로, `👋 hi-vibe` 접두사는 고정 — 나중에 세션에서 grep 가능하게):
- `👋 hi-vibe가 방금 <무엇>을 잡았어요 — <이 스킬/에이전트>.` (고쳐줬으면 "…을 잡아서 고쳤어요.")
+ 리뷰·테스트·벤치마크 결과에 변경 전후 수치, 백분율, 정확도, 처리량, 지연 시간
+ 같은 **정량 주장**이 있을 때만 `references/metrics-evidence.md`를 읽는다.
+ 그 조건을 만족하면 docs-keeper의 evidence 모드로 근거를 기록하고 CHANGELOG에서
+ 연결한다. 숫자가 없거나 비교 조건이 다르면 이 참조를 읽거나 수치를 만들지 않는다.
- 여기서 세는 것은 체크리스트 ⚠️ 중 **사용자가 그대로 배포했을** 항목이다.
- fresh-eyes 항목은 그 출력이 자체 catch 줄을 이미 달고 오므로, 그건 그대로
- 전하고 여기서 또 달지 않는다(중복 금지).
+ ## 공통 완료 계약
- **과장 금지(핵심):** 셋 중 하나라도 아니면 이 줄을 넣지 마라. 통과·스타일
- 지적·사용자가 이미 알던 것에 공을 붙이는 순간, 조용히 일하는 플러그인의
- 신뢰가 깨진다. 이 표기는 자화자찬이 아니라, 티 안 나게 작동하는 hi-vibe가
- *실제로 무엇을 살렸는지*를 저자·사용자가 볼 수 있게 하는 창이다.
+ - 실제로 실행한 검증만 말한다. 못 했으면 이유와 함께 “실행 검증 안 됨”이라고 쓴다.
+ - 구조·제약·실질 동작 변화는 docs-keeper 규칙에 맞춰 같은 턴에 기록한다.
+ - 사용자가 그냥 지나쳤을 진짜 결함을 hi-vibe가 새로 찾아낸 경우에만
+ `👋 hi-vibe가 방금 <무엇>을 잡아서 고쳤어요 — <스킬/에이전트>.`를 붙인다.
+ 통과, 취향, 사용자가 이미 발견한 문제에는 붙이지 않는다.