PR 데모
리뷰어는 diff를 열지 않고도 1분 이내에 변경 사항을 이해해야 합니다: 무엇이 변경되었는지, 지금 어떻게 동작하는지, 작동 증명, 어디를 봐야 하는지. 저장소 정보 — 캡션 언어, 변경된 흐름 녹화 방법, 게시 위치 — 는 .claude/profiles/delivery.md (Demo, Notify)에 있습니다.
완벽함보다 짧고 직설적입니다. 30초 안에 다섯 장면을 다음 순서로 배치하며, 변경 사항에 해당 장면이 없으면 빈 공간을 만들지 말고 생략하세요:
- 변경된 내용 — 제목과 최대 세 개의 불릿.
- 지금 동작 방식 — 흐름과 변경된 노드 표시.
- 전후 비교 — 시각적으로 확인 가능한 경우에만; 백엔드 변경 시 생략.
- 수정본 실행 — 녹화 영상, 중요한 부분만 자르고 12초 미만.
- 증명 — 리뷰 키의 검사 결과 그리고 테스트를 하나의
key장면에 함께 표시. 둘은 같은 질문에 답하므로 각각 별도 장면을 사용하지 않습니다.
1. 자료 수집
out/demo/<pr 또는 issue>/ 디렉토리(git-ignored)에 저장:
- 리뷰 키의 증거:
out/review-key/<n>/result.json및key-reviewer판정(review-key). - 사용자에게 보이는 변경의 스크린샷: 동일한 뷰를 변경 전(기준 브랜치)과 후, 같은 크기와 데이터로 촬영. 변경된 부분만 자르세요; 1280px 전체 페이지는 변경 사항이 너무 작아 보입니다.
- 변경된 흐름의 녹화 (프로필의 Demo 명령어, 또는 Playwright로 e2e 검사 단계를
browser.newContext({ recordVideo: { dir, size: { width: 1280, height: 800 } }, viewport: { width: 1280, height: 800 } })로 감싸서 실행; 컨텍스트가 닫힐 때 파일이 생성됩니다). 20초 미만으로 유지: 중요한 화면에서 시작하고, 로그인 과정은 촬영하지 마세요. - 변경 사항의 흐름을 mermaid
flowchart LR(또는TD)로: 5~9개 노드, 사용자 또는 요청의 경로, 변경된 부분에:::changed, 새로 추가된 부분에:::added, 제거된 부분에:::removed표시. 파일이 아닌 동작을 설명하세요. - 실행된 테스트들, 각각 숫자 하나씩 (
476 tests,12 flows).
2. demo.json 작성
자료와 같은 위치에 (경로는 상대 경로). 텍스트는 프로필의 캡션 언어로; 짧게 — 장면은 몇 초 안에 읽힙니다. 백틱과 **bold**가 작동합니다.
{
"lang": "en",
"brand": "**shop** · PR #352",
"ref": "Closes #331",
"kicker": "Bugfix",
"title": "The checkout fits at 1280 px",
"subtitle": "The pay button covered the basket, and the coupon field was 49 px wide.",
"chips": [{ "text": "Review key: PASS", "ok": true }, "3 files"],
"scenes": [
{ "type": "title", "seconds": 3 },
{ "type": "bullets", "items": ["…", "…", "…"], "seconds": 5 },
{ "type": "flow", "mermaid": "flowchart LR\n A[Width] --> B{\"< 1440 px?\"}\n B -- yes --> C[Icons]:::changed", "seconds": 6 },
{ "type": "compare", "before": "before.png", "after": "after.png", "seconds": 5 },
{ "type": "clip", "heading": "Recorded at 1280 px", "src": "run.webm", "caption": "e2e: checkout-fit", "speed": 1.5, "maxSeconds": 12 },
{ "type": "key", "result": "../../review-key/331/result.json", "baseFails": true, "seconds": 7,
"rows": [{ "ok": true, "text": "`verify.sh` · 476 tests" }] },
{ "type": "end", "title": "Ready for review", "lines": ["PR: **github.com/…/pull/352**", "3 am: …"], "seconds": 3 }
]
}
장면 유형: title, bullets (items, 최대 네 개), flow (mermaid, legend: false로 범례 숨김), compare (before, after, 레이블; layout: "stack"은 툴바처럼 넓은 스트립을 위해 위아래로 배치), clip (src .webm/.mp4, from/to 초, speed, maxSeconds 기본 20, crop {x, y, w, h} (녹화 픽셀 단위)로 중요한 부분에 확대; 길이는 파일에서 가져옴), key (result = keycheck의 result.json 또는 객체; verdict, baseFails, claim, rows 및 count {value, total, label} 로 재정의 가능 — 예: 8개 케이스를 실행하는 하나의 검사는 8/8로 표시하는 것이 더 읽기 좋음), checks (items with name, detail, ok), end (title, lines). 모든 장면은 heading, kicker, seconds를 가질 수 있습니다. 변경 사항에 없는 것은 생략하세요 — 백엔드 변경에는 compare나 clip이 없습니다; 순수 리팩터링은 key 장면에 동등성 검사와 몇 가지 정보만 있습니다.
key 장면의 왼쪽 패널은 최대 8개의 행을 담을 수 있으며, 키의 검사 항목보다 먼저 rows를 표시하므로 테스트는 각각 한 행("verify.sh · 476 tests")으로 여기에 넣고, 이후 checks 장면을 사용하지 마세요. 리뷰 키가 없을 때만 별도의 checks 장면을 사용하세요.
표현
캡션은 화면에 보이는 몇 초 안에 읽히므로, 모든 단어가 무게를 가져야 합니다. 리뷰어를 짜증나게 했던 데모에서 배운 세 가지 규칙:
- 결과를 말하고, 칭찬하지 마세요. 판정 스탬프와 "checked independently by key-reviewer"는 이미 키가 통과했고 누가 검사했는지 알려줍니다. "PASS, no cheating"이나 "fully verified"를 추가하면 작성자가 자기 숙제를 채점하는 것처럼 읽힙니다. 판정에 대해 절대 편집하지 마세요.
- 숫자 레이블은 단순 명사로:
checks pass,cases identical,tests. 레이블에fail/faller같은 동사를 사용하면 "5/5"가 거꾸로 읽히는 문장이 됩니다 — "5 of 5 fail"은 변경 사항이 망가진 것처럼 들리지만 실제로는 기준 브랜치가 실패한 것을 의미합니다. 기준 브랜치 실패를 말해야 한다면baseFails행이 있으며, 스스로 표현됩니다. - 군더더기 없음. "성공적으로", "예상대로", "이것은 보장합니다"와 같은 표현, 그리고 제목을 다시 설명하는 불릿은 제거하세요. 불릿이 리뷰어로 하여금 "네, 당연하죠"라고 말하게 만든다면, 그것은 불릿이 아닙니다.
3. 렌더링
node <이 스킬의 디렉토리>/scripts/render-demo.mjs out/demo/<n>/demo.json [--webm]
Chromium에서 프레임 단위로 모든 장면을 그리고(선명한 텍스트, 드롭된 프레임 없음) demo.mp4 (H.264, Slack, GitHub, 브라우저에서 인라인 재생), demo.png (포스터: 흐름 또는 키 장면)를 인코딩하며, --webm 옵션을 주면 VP9 코덱의 demo.webm도 생성합니다. ffmpeg가 필요하고, 프로젝트에서 Playwright를 가져오거나 없으면 ~/.cache/carnufex-showcase에 한 번 설치합니다. 렌더링 시간은 비디오 1초당 약 1초입니다.
보내기 전에 확인하세요: 각 장면의 프레임을 추출하여(ffmpeg -ss <t> -i demo.mp4 -frames:v 1 f.png) 확인합니다. 텍스트 잘림, 빈 다이어그램, 로그인 화면 클립 등이 있으면 수정하고 다시 렌더링하세요.
4. 게시
프로필의 Notify가 지정한 채널에 봇으로 게시합니다. 그래야 실제 알림이 전달됩니다(Slack 커넥터를 통해 게시된 메시지는 본인으로부터 온 것으로 표시되며, Slack은 자신의 메시지에 대해 누구에게도 알리지 않습니다). PR당 하나의 스레드: 데모가 PR의 스레드를 시작하고, 그 이후의 모든 것(CI, 리뷰 라운드, 병합, 배포, 라이브 체크, 롤백)은 해당 스레드에 답글로 게시됩니다(ship이 게시). 채널은 모든 저장소에 걸쳐 PR당 한 줄로 유지됩니다. 메시지를 파일에 쓰세요; 독립적으로 동작해야 하며(Slack mrkdwn), 저장소 이름으로 시작해야 합니다:
*<repo>* · PR #<n> · <title> — ready for review
<PR url>
• <변경된 내용, 줄 1>
• <줄 2>
Review key (<kind>): *PASS*, checked independently: <판정에서 한 줄>
3 am: <signal>
node <이 스킬의 디렉토리>/scripts/post-slack.mjs --channel <id> --pr <PR url> --file out/demo/<n>/demo.mp4 --title "<repo> PR #<n>: <title> (<seconds> s)" --comment-file out/demo/<n>/message.md
node <이 스킬의 디렉토리>/scripts/post-slack.mjs --channel <id> --pr <PR url> --text-file update.md [--broadcast]
--pr는 채널에서 PR URL로 스레드를 찾습니다(상태 저장 안 함): 찾으면 해당 스레드에 게시하고, 찾지 못하면 새 스레드를 시작합니다. --text-file은 파일 없이 메시지를 게시합니다; --broadcast는 스레드 답글을 채널에도 표시합니다(결과(라이브 또는 롤백)에 사용). 봇 토큰은 SLACK_BOT_TOKEN 환경 변수 또는 slack-channel 플러그인의 ~/.claude/channels/slack/.env에서 가져옵니다. 봇은 채널에 있어야 합니다(not_in_channel: /invite @<bot>). 봇이 없으면 Slack 커넥터로 대체하여 파일 업로드 API를 호출하고, 알림이 전송되지 않음을 명시합니다.
같은 세 줄, mermaid 플로우차트 및 Slack 링크를 PR 설명에도 추가합니다(GitHub는 mermaid를 렌더링하지만 API를 통해 업로드된 비디오는 재생하지 않습니다).