패치 대상 오진 1건: 도달 불가 코드의 식별
버그를 하나 찾았다고 생각했다.
웹소설 집필 툴킷을 만들고 있다 — Claude Code 플러그인이다. 거기엔 "지금 뭐 해야 하나?"를 물으면 다음 할 일을 알려주는 기능이 있다. 판단 순서가 이렇다.
...
원고가 0편이면 → "기획부터 하세요"
...
원고가 30편 이상이고 설정집이 없으면 → "설정집 만드세요"
...
조건을 위에서부터 확인해 첫 번째로 걸리는 것을 답한다.
그런데 "원고 없이 설정 결정만 검사하는 모드"가 따로 있다. 그건 원고가 0편일 때 쓰는 것이다. 첫 번째 조건에서 다른 데로 보내버리니 영원히 안내되지 않는다 — 고 생각했다.
코드를 읽고 확신했다. 만들어 놓고 이정표를 안 세운 것이라고. 패치도 준비했다.
(먼저 밝혀둘 것. 이 확신부터가 틀렸다. 그 모드는 스킬 설명을 통해 이미 도달 가능했고, 검토자가 시험 발화를 돌려서 보여줬다. 내가 "도달 불가"라고 단정한 근거는 라우터 코드 하나뿐이었다.)
검토자에게 넘겼다.
"출력이 한 글자도 안 바뀝니다"
검토자는 가짜 프로젝트를 다섯 개 만들어 실제로 돌려 봤다. 패치를 적용한 사본과 원본을 비교했다.
같았다. 완전히.
그 위에 답이 있었다.
if r and docs: # 설정이 있고 AND 원고가 있으면
원고가 0편이면 이 조건이 거짓이 되어 "설정 없음"으로 처리된다. 그러면 "설정이 없으면" 분기가 먼저 잡아버린다. 내가 고치려던 "원고가 0편이면" 줄까지 내려오지도 않는다.
두 조건이 상호배타였다. 내가 고치려던 줄은 애초에 실행되지 않는 줄이었다.
이건 코드를 읽어서는 안 보인다. 두 함수를 함께 실행해야 드러난다.
진짜 버그는 더 앞이었고 더 나빴다
그 한 줄 때문에 무슨 일이 일어나고 있었냐면.
$ 프로젝트 초기화 실행
✓ 설정 파일 생성됨
$ "지금 뭐 해야 해?"
→ "프로젝트 초기화하세요. 설정과 디렉토리를 만드세요."
방금 한 일을 다시 하라고 한다. 새 사용자가 보는 첫 화면이다.
그리고 하나 더. 설정을 무시하고 폴백으로 가면 경로도 같이 버린다. 설정에 "내 설정집은
설정/ 폴더에 있어"라고 적어 뒀어도, 폴백은 bible/ 을 찾는다. 없다. 그래서 "설정집
없음"이라고 보고한다.
폴더 이름을 한글로 바꿨을 뿐인데 툴킷이 못 찾는다.
그 코드는 자기가 뭘 해야 하는지 적어 놓고 있었다
이상한 건 여기부터다. 그 함수의 설명문에 이렇게 적혀 있었다.
설정이 있으면 그 경로를, 없으면 관례를 쓴다.
그리고 폴백 쪽 주석에는.
폴백 — 설정도 원고도 못 찾은 경우
둘 다 "설정이 있으면 쓴다"고 말한다. 코드만 "설정 그리고 원고"였다.
의도는 처음부터 문서에 있었다. 구현이 어긋났고, 아무도 대조하지 않았다.
검사는 왜 못 잡았나
두 가지가 겹쳤다.
테스트 데이터가 늘 원고 10편이었다. 원고 0편인 경우가 한 번도 안 돌았다. 버그가 사는 자리를 테스트가 방문한 적이 없다.
두 번째가 더 나쁘다. 상태 안내를 검사하는 코드가 이랬다.
expect_out=['다음 할 일']
출력에 "다음 할 일"이라는 글자가 있는지만 본다. 그 답이 무엇인지는 아무도 안 본다.
"프로젝트 초기화하세요"라는 틀린 답에도 "다음 할 일"은 들어 있다. 검사는 초록이다.
고쳤다. 테스트 데이터에 원고 0편 프로젝트를 추가하고, "나오면 안 되는 문자열"을 지정할 수 있게 했다. 그리고 고친 걸 되돌려서 검사가 실제로 빨개지는지 확인했다.
세 번째로 내가 틀렸다
같은 작업에서 순환 참조 검사도 다뤘다. 설정 A가 B에 기대고 B가 다시 A에 기대는 상태다.
기록 절차에 판정 방법을 넣고, 사용자가 그 질문을 하면 거기로 가는지 시험했다.
"설정 둘이 서로를 딛고 서 있는 것 같은데" → 다른 스킬로 갔다.
틀렸다고 판단하고 설명문을 고치려다 멈췄다.
그게 맞는 답이었다. 순환을 진단하는 건 감사 스킬 일이고, 기록 절차는 예방이다. 다른 순간에 하는 다른 일이다. 사용자가 "이미 꼬였는데 봐줘"라고 하면 감사 쪽이 맞다.
여기서 설명문에 키워드를 채웠으면, 바로 전 릴리스에서 만든 검사가 막으려던 짓을 하루 만에 다시 하는 거였다. 그 검사는 "시험 발화를 설명문에서 베끼지 마라"를 강제한다.
대신 감사 스킬이 기록 절차의 판정 기준을 가리키게 했다. 같은 기준을 두 곳에 적지 않는다.
그리고 내 수정이 새 버그를 만들었다
고쳤다고 생각한 뒤에 검토자를 또 붙였다. 이번엔 회귀만 보라고 했다.
원고 폴더 이름을 ms 에서 원고 로 바꾼 작가가 있다고 하자. 설정에는 아직 ms 라고
적혀 있다. 흔한 일이다.
고치기 전에는 "원고 10편 있음 → 등록하세요"가 나왔다. 고친 뒤에는 "원고 없음 → 로그라인부터 잡으세요"가 나온다.
고치기 전에는 설정을 안 믿고 폴더를 통째로 뒤졌다. 그래서 우연히 구제하고 있었다. 설정을 믿게 만들자 그 구제가 사라졌다. 원고 10편을 가진 작가를 기획 단계로 되돌려 보낸다.
폴백은 "설정이 없을 때의 경로"였는데 실제로는 "설정이 틀렸을 때의 구제"도 겸하고 있었다. 아무 데도 그렇게 적혀 있지 않았다. 버그를 고치면서 적혀 있지 않은 다른 일을 같이 없앴다.
지금은 이렇게 나온다.
설정이 가리키는 경로에 원고가 없다
설정 밖에서 원고로 보이는 파일 10개를 찾았다.
`/wn-adopt --force` 로 지금 구조를 다시 등록하라.
고친 것이 새 고정 화면이 됐다
또 하나. 되살린 그 분기가 이제는 원고 0편 구간 전체의 고정 화면이 됐다.
설정을 다 짜고 인물표를 채우고 전개를 짜도, 원고를 한 편도 안 썼으면 계속 "로그라인부터 잡으세요"가 나온다. 죽어 있을 땐 안 보이던 문제가 살아나면서 드러났다.
기획 자료가 있는지로 갈랐다. 없으면 로그라인부터, 있으면 1화를 쓰라고.
남는 것
한 릴리스에서 내 판단이 다섯 번 뒤집혔다.
- 내가 만든 안이, 내가 이미 기각한 안의 다른 이름이었다
- 내가 "도달 불가"라고 단정한 것이 실은 도달 가능했다
- 내가 고치려던 줄이 실행되지 않는 줄이었다
- 내 수정이 멀쩡하던 경로를 깼다
- 내가 "같은 기준"이라고 쓴 두 문장이 같은 입력에서 다른 답을 냈다
그중 둘째·셋째·넷째는 읽어서가 아니라 돌려 봐서 드러났다. 가짜 프로젝트를 만들고, 가짜 데이터를 넣고, 패치 전후를 비교하고.
코드 리뷰가 잡는 것과 실행이 잡는 것은 다르다. 상호배타 조건, 도달 불가능한 분기, 테스트가 한 번도 방문하지 않은 상태 — 이건 읽어서는 안 보인다.
그리고 하나 더. "고쳤다"는 기록은 남는데 동작이 안 바뀌는 수정이 있다. 실행되지 않는 줄을 고치면 그렇게 된다. 패치노트에는 고쳤다고 적히고, 6개월 뒤에 누가 그 기록을 근거로 쓴다.
webnovel-toolkit v1.7.0 작업 기록. 이 툴킷이 무엇인지는
네 번의 릴리스에서 배운 것에 적어 뒀다.