# Gotchas: 자주 생기는 함정 항목마다 증상, 원인, 예방, 하네스 검사 ID를 적습니다. 하네스 검사가 있는 항목은 `python3 harness/gate.py <단계> <프로젝트>`가 막고, 없는 항목은 사람이 확인합니다. 새로 겪은 함정은 같은 형식으로 끝에 추가하고, 가능하면 `harness/gate.py`에 검사를 더한 뒤 `harness/selftest.sh`에 막아야 할 사례를 함께 넣습니다. ## 일관성 **G01. 클립 시작은 같은데 뒤에서 인물이 바뀐다** - 증상: 키프레임과 0초 프레임은 같은데 1~2초 뒤에 상의 색, 안경, 머리가 바뀝니다. 실제 클립에서 0초는 주황 상의, 1.0초부터는 검은 상의로 바뀐 사례를 확인했습니다. - 원인: 영상 생성은 시작 화면만 고정하고 이후 프레임은 새로 만듭니다. - 예방: 키프레임, 0, 1.0, 1.5, 2.0, 3.0초를 한 줄에 놓고 비교합니다. 0초 프레임만 보고 통과시키지 않습니다. - 하네스: `clips` 단계가 프레임 스트립(`qa/strip-<클립>.png`)을 만들고(V07), 눈으로 본 기록과 클립별 판정이 없으면 막습니다(V08, V09, V10). **G02. SSIM 같은 숫자로 인물 변형을 잡을 수 없다** - 증상: 키프레임과의 SSIM이 정상 클립에서도 시간이 지나면 떨어집니다. 실제 클립 16개 중 7개가 1.0~3.0초 지점에서 0.35 미만이었고, 인물이 유지된 클립도 0.16까지 내려갔습니다. 옷이 바뀐 클립의 최저값은 0.39였습니다. - 원인: 전체 화면 SSIM은 인물의 움직임과 인물의 변형을 구분하지 못합니다. - 예방: 시작 화면 일치(0초)만 숫자로 막습니다. 같은 쌍의 0초 최저값은 0.705, 다른 쌍 240개의 최고값은 0.415여서 임계값 0.55로 구분됩니다. 뒤쪽 프레임은 눈으로 봅니다. - 하네스: V05는 BLOCK, V06은 참고값으로만 출력합니다. **G03. 외형 고정문이 장면마다 조금씩 달라진다** - 증상: 안경, 옷, 머리색이 바뀝니다. - 예방: `character.txt`를 만들고 모든 프롬프트에 파일 내용을 그대로 붙입니다. 손으로 다시 쓰지 않습니다. 옷 색을 문장에 적습니다. - 하네스: `character` 단계 C01~C05 (파일 존재, 1개 문단 평문, 최소 길이, 시트 이미지, 색 표현). **G04. 음성 ID가 대사마다 달라진다** - 증상: 장면마다 다른 사람이 말합니다. - 예방: `voices.json`에 `voice_id`를 한 번만 적고 모든 대사 생성이 그 값을 읽게 합니다. - 하네스: `audio` 단계 A01~A04. ## 기획과 생성 **G05. 한 장면에 행동이 둘 이상이다** - 증상: 손, 소품이 변형됩니다. 노트북이 열렸다 닫히고 다시 열립니다. - 예방: 한 장면에는 행동 1개와 끝 상태를 적습니다. - 하네스: 없음. `templates/SCENE_AUDIT.md`의 결함 체크리스트로 확인합니다. (장면 표의 빈 칸은 S05가 막습니다.) **G06. 같은 키프레임을 두 번 쓴다** - 증상: 보는 사람은 반복 장면으로 느낍니다. - 예방: 컷마다 다른 키프레임을 씁니다. - 하네스: `edit` 단계 B04(`scripts/dupcheck.py`), `keyframes` 단계 K02(파일 내용이 같은 중복). **G07. 클립 뒤쪽이 녹는다** - 증상: 얼굴, 소매가 흐물거립니다. 길수록 심합니다. - 예방: 클립은 3~6초로 짧게 만들고, 쓸 수 있는 앞쪽 구간만 잘라 씁니다. - 하네스: V03(권장 3~6초 밖은 경고, 1~8초 밖은 막음), B06(컷이 클립 길이보다 뒤까지 쓰면 막음). **G08. 렌더나 생성이 중간에 끊겼는데 성공처럼 보인다** - 증상: 파일은 있는데 프레임이 모자랍니다. - 예방: 프레임 수를 `fps x 길이`와 비교합니다. - 하네스: V04(클립), F02·F08·F09(최종본), `scripts/mix-mux.sh`의 프레임 수 게이트. **G09. 이미지 도구가 같은 파일을 두 번 저장한다** - 예방: 파일 해시로 중복을 걸러냅니다. - 하네스: K02. **G10. 키프레임과 클립의 화면비가 다르다** - 증상: 영상 생성이 키프레임을 잘라 쓰거나 늘려서 인물 비율이 달라집니다. - 예방: 키프레임과 클립의 화면비를 맞춥니다. - 하네스: K03(짝이 있는 키프레임과 클립의 화면비 차이 3% 초과는 막음), K04(키프레임끼리 섞여 있으면 경고). ## 박자와 조립 **G11. 컷 길이를 초로만 적으면 박에서 벗어난다** - 증상: 장면 전환이 음악과 점점 어긋납니다. - 예방: BPM에서 박 길이를 계산하고(60 ÷ BPM) 컷 길이를 박의 배수로 정합니다. - 하네스: `edit` 단계 B02(컷 경계가 박 격자의 반 프레임 이내인지). **G12. 컷 길이를 반올림해 더하면 오차가 쌓인다** - 증상: 컷 수십 개가 지나면 박에서 한 프레임 이상 벗어납니다. - 예방: 각 컷의 시작 프레임은 누적 시간에서 한 번만 반올림하고, 컷 길이는 다음 시작 프레임과의 차이로 구합니다. - 하네스: B02는 길이가 아니라 경계(시작과 끝)를 검사하므로 프레임 단위로 반올림한 편집표도 통과합니다. B03은 컷 연결을 봅니다. **G13. 대사가 컷보다 길다** - 증상: 입이 말하는 도중에 잘립니다. - 예방: 대사 길이를 먼저 재고 컷 길이를 정합니다. - 하네스: B05 (`edit.json` 컷에 `"line": "n1"`을 적으면 검사. voices.json에 없는 id는 막음). **G14. 생성된 곡의 BPM이 요청한 값과 다르고 구간마다 달라진다** - 증상: 음악 생성 도구에 요청한 BPM과 실제 곡이 다릅니다. 생성한 곡 6개는 모두 요청한 값보다 빨랐고, 144 BPM을 요청한 곡은 처음 144.90에서 끝 부분 147.39까지 빨라졌습니다. 곡 전체의 평균값 하나로는 격자에 맞지 않습니다. - 예방: 구간별로 실제 BPM을 측정하고, 구간마다 `atempo`(요청 BPM ÷ 잰 BPM)로 보정한 뒤 격자에 올립니다. ffmpeg 9.0.2의 `atempo`는 0.5~100을 받습니다. - 하네스: 없음. **G15. 컷 하나만 바꿨는데 뒤 컷이 모두 밀린다 / 첫 컷 앞이 비어 있다** - 예방: 컷을 자르거나 늘리면 이후 컷의 시작 시간을 다시 계산하고 `edit` 게이트를 다시 돌립니다. - 하네스: B02, B03(첫 컷이 0초에서 시작하는지, 컷 사이에 빈틈·겹침이 없는지. 컷은 시작 시간순으로 정렬해서 검사). **G16. 컷 속도를 0.9~1.1배 밖으로 바꾼다** - 증상: 동작이 부자연스럽습니다. - 예방: 속도 조정은 0.9~1.1배 안에서만 합니다. - 하네스: 없음. ## 소리 **G17. loudnorm을 한 번만 적용하면 목표값에서 어긋난다** - 증상: 짧은 클립에서 목표와 0.5~1.5 LUFS 벗어납니다. - 예방: 최종 mp4에서 다시 측정하고 차이를 그대로 보고합니다. - 하네스: `final` 단계 F05(목표 ± 1.5 LUFS). **G18. AAC로 인코딩하면 true peak가 올라간다** - 예방: mux 후 true peak를 다시 측정하고 -1.5 dBTP 이하로 여유를 둡니다. - 하네스: F06(true peak가 -1.0 dBTP를 넘으면 막음). **G19. 오디오가 영상보다 먼저 끝나거나 영상과 fps가 다르다** - 증상: 뒤쪽이 무음이거나 싱크가 밀립니다. - 예방: mux 후 영상·오디오 길이와 fps를 `edit.json`과 비교합니다. - 하네스: F03, F08(fps), F09(오디오와 영상 길이). **G20. 직접 듣지 않은 것을 들었다고 쓴다** - 예방: "받아쓰기와 라우드니스로 확인했다"고 씁니다. - 하네스: 없음. ## 도구 **G21. 파이프로 출력을 자르면 종료 코드가 사라진다** - 증상: `검사.sh | tail`을 쓰면 검사가 실패해도 `$?`는 0입니다. 파이프의 종료 코드는 bash에서 `PIPESTATUS`, zsh에서 `pipestatus`로 읽는데, bash에서 `pipestatus`, zsh에서 `PIPESTATUS`처럼 바꿔 쓰면 빈 값이 나옵니다. - 예방: 종료 코드로 판정할 때는 파이프를 쓰지 않고 파일로 저장한 뒤 읽습니다. 하네스는 보고서 파일(`qa/gate-<단계>.txt`)을 남깁니다. **G22. 크로마키가 민트·청록 색까지 지운다** - 원인: `chromakey`는 YUV 기준입니다. - 예방: RGB 기준의 `colorkey`를 씁니다(`scripts/key-plate.sh`). **G23. ffmpeg 빌드에 drawtext가 없을 수 있다** - 예방: 글자는 Remotion에서 올립니다. **G24. whisper가 메모리 부족으로 멈춘다** - 예방: 멈춘 프로세스를 종료하고 작은 모델로 다시 실행합니다. **G25. h264로 인코딩한 영상은 같은 프레임도 바이트가 달라진다** - 증상: md5로 반복 프레임을 찾으면 놓칩니다. - 예방: 차이 비교(임계값)나 렌더된 PNG 비교를 씁니다. **G26. ffmpeg 필터에서 `adelay` 뒤 `apad`와 `atrim`을 쓰면 지연이 사라진다** - 증상: 음악을 박 격자의 지정 시간에 놓으려고 `adelay`를 썼는데 출력이 목표 길이보다 짧고 음악이 0초에서 시작합니다. ffmpeg 9.0.2에서 합성 음원으로 재현했습니다(10.333초로 끝남). - 예방: 마지막 `atrim` 앞에 `asetpts=N/SR/TB`를 넣습니다. 이 형태는 12.000초로 끝나고 무음이 1.667초에 끝났습니다. (`references/beat-grid.md`) - 하네스: 없음. 조립 후 길이를 `ffprobe`로 확인합니다. **G27. ICC 프로파일이 붙은 영상에서 `ffprobe -of csv=p=0`이 프레임 수를 `30,`처럼 출력한다** - 증상: 프레임 수 비교가 실패해 정상 영상이 막히거나 `mix-mux.sh`가 중단됩니다. Remotion 기본 설정(JPEG 프레임)으로 렌더한 영상에서 생깁니다. - 원인: 프레임 부가 데이터(side data)가 있으면 csv 출력에 빈 필드가 붙습니다. - 예방: 값 하나를 읽을 때는 `-of default=nw=1:nk=1`을 씁니다. Remotion은 `remotion.config.ts`에서 프레임 형식을 PNG로 지정합니다. - 하네스: V04, F02, `scripts/mix-mux.sh`, `scripts/verify.sh`가 이 형식으로 읽습니다. `selftest.sh`에 회귀 사례가 있습니다(PIL이 있을 때). ## 보고 **G28. 렌더가 끝난 것과 영상이 좋은 것은 다르다** - 예방: 컨택트 시트를 직접 보고, 본 결과를 `qa/review.md`에 남깁니다. - 하네스: `final` 단계 F07(REVIEWED 줄과 본 결과), F10(검수 기록이 최종본보다 나중에 작성됐는지). **G29. 무료 플랜으로 만든 음악·목소리를 상업 공개에 쓴다** - 예방: 상업 공개용은 유료 플랜에서 다시 생성합니다. 조건은 `references/licensing.md`에서 작업 직전에 다시 확인합니다. - 하네스: 없음.