2026-10-28 (수) 개발일지
오늘은 세 가지가 걸렸다. 비용 집계가 틀렸던 것, 자동완성 패널이 검게 보이던 것, 그리고 발행된 글에 원본에 없는 문장이 섞여 있던 것. 앞의 둘은 원인이 한 줄로 떨어졌고, 마지막은 수집 단계까지 거슬러 올라가야 했다.
하네스 — 비용이 매 턴 늘기만 한 건 누적값을 다시 더하고 있어서였다
지난번에 실행 비용이 예상의 5배를 넘었다고 정리했는데, 그 숫자부터가 틀렸다. 14개 대화를 다시 훑어보니 비용이 매 턴 늘기만 하고 줄어드는 구간이 하나도 없었다. 정상이라면 턴마다 그 턴의 사용량만큼만 붙어야 하는데, 그렇지 않다는 뜻이었다.
세션을 이어 쓸 때 보고되는 비용은 그 세션의 누적값이었다. 나는 그 누적값을 턴마다 다시 더하고 있었다.
화면에 찍힌 값과 실제를 맞춰 보니 차이가 컸다.
| 실행 | 화면에 찍힌 값 | 실제 비용 |
|---|---|---|
| 10-23 (각 1회) | $23.4 | 약 $5.3 |
| 이번 (각 3회) | $82.6 | 약 $15.3 (riff $8.8 · 단독 $6.5) |
이번 실행은 미리 잡아 둔 예상 $30~45보다도 적게 나왔다. 계산 방식을 고쳐 커밋했다.
하네스 — eval이 통과하지 못한 규칙은 문구가 아니라 도구 호출 형태 문제였다
riff가 사용자에게 내부 용어를 흘리는 문제가 있었다. 3회 모두 “CANVAS”가 나왔고, 한 번은 “복잡 depth”, “FRAME”, “A-007”까지 그대로 갔다. 단독 실행에서는 0회였다. 비개발자에게는 그대로 혼란이다.
먼저 plain-language eval을 돌렸다. 켠 쪽 4회 중 2회가 no-jargon: pattern found로 실패했다. 수정 전 실패(red)가 확인된 셈이라 규칙을 넣고 다시 돌려 4/4로 통과시켰다.
여기서 끝이 아니었다. 업종 팩을 읽히는 규칙은 서술형 문구로는 0/3이었다. “~/.riff/domains를 찾는다”라고 적어 두면 규칙을 읽고도 실제로 찾지 않았다. Glob(pattern: "*.md", path: "~/.riff/domains")처럼 도구 호출 형태로 적자 3/3이 됐다.
규칙은 “무엇을 한다”가 아니라 “어떤 도구를 어떤 인자로 부른다”로 적어야 먹혔다.
덤으로 알게 된 것도 있다. eval 환경은 플러그인 참고 문서(references/) 읽기를 막는다. 그래서 eval로 검증되는 규칙은 사실상 SKILL.md에 적힌 것뿐이고, 참고 문서 쪽 규칙은 실제 사용에서만 돈다.
게임 — 자동완성 패널이 검게 보인 건 테마가 팝업을 아예 안 칠해서였다
코드 자동완성 패널이 검은 막대에 어두운 글자로 깨져 보인다는 제보가 있었다. 색을 바꾸기 전에 Godot가 실제로 노출하는 항목 이름과 기본값부터 확인했다. 추측으로 테마를 만지면 어디를 고쳤는지도 모르게 된다.
숫자가 바로 나왔다. completion_background_color 기본값이 (0.17, 0.16, 0.2)로 #2B2933, 거의 검정이었다. 옵션 글자색은 C_TEXT = #3C3C3C이고 python_completion_controller.gd:88에서 명시적으로 넘기고 있었다. 명암비 약 1.2:1이니 안 읽히는 게 당연했다.
원인은 색값이 아니라 적용 경로였다. 팝업은 PopupPanel이 아니라 CodeEdit이 직접 그리는 개체라서, _apply_editor()가 에디터 색을 다 깔아도 팝업만 Godot 기본값에 남아 있었다. theme_setup.gd:227에 팝업 5색과 completion 스타일박스를 추가했다.
재발 방지가 더 까다로웠다. 명암비만 검사하면 “테마가 그 색을 아예 안 깔았다“는 원래 결함을 못 잡는다. 그래서 theme_contract_test.gd에 Godot 기본값과 같으면 실패하는 검사 11개를 넣었다. 179 checks 0 failed, 전체 20/20 통과.
검증 중에 캡처 스크립트가 Identifier not found: ThemeSetup으로 죽었다. --script 메인 루프는 autoload 등록 전에 컴파일되기 때문이었다. 런타임 접근으로 바꾸자 캡처가 돌았고, 수정 전 화면(검은 막대)과 수정 후 화면(흰 카드 + 테두리, 입력 접두사에 연두 띠)이 나란히 찍혔다.
옵시디언 정리 — 발행된 글에 로그에 없는 문장이 섞여 있었다
발행된 글을 원본 기록과 대조하니 없던 장면이 있었다. “앱을 띄워 3분할 드래그를 반복해봤다”고 썼는데 원본에는 “이번 빌드는 실제 앱으로 띄워 보지 않았습니다”라고 적혀 있었다. 원인 추적 서사 전체가 지어낸 것이었다.
왜 그런지 입력 쪽을 재 봤다. 최근 7일 동안 글 생성에 넘기는 발췌 줄 가운데 오류·원인을 언급하는 줄이 3~14%뿐이었다. 이유는 네 가지였다. 도구 실행 결과를 아예 수집하지 않았고, assistant 문장을 300자에서 자르고 다시 250자에서 잘랐고, 세션당 6줄을 내용과 무관하게 처음·중간·끝 위치로 골랐고, 마지막 50개 메시지만 봤다. 게다가 세션 58개 중 48개가 임시 폴더에서 돌아간 평가 시뮬레이션이었다.
입력에 원인이 없으니 생성 단계는 “전환점이 1~2개 있어야 한다”는 요구를 지어내서 채웠다. 날조 금지 규칙보다 형식 요구가 세게 작동한 것이다.
수집 단계에서 서브에이전트 기록과 임시 폴더 세션을 빼고, 자르기를 없애고, 실패한 도구 결과를 [오류] 명령 → 메시지 형태로 함께 넣었다. 프롬프트에서는 전환점과 가설을 로그에 있을 때만 쓰게 바꿨다.
발행 전 검사는 두 단계로 나눴다. 먼저 글 전체를 버리는 검사(프롬프트 문구 유출, 같은 제목 반복, 작성 메모 노출)를 10월 발행 글 10편에 돌렸다. 문제 있던 5편이 62~81%로 걸리고 정상 5편은 0~31%로 갈렸다. 처음 기준 30%에서는 10-19가 잘못 걸려서 50%로 올렸다.
그다음 문장 단위로 바꿨다. 문장마다 숫자와 코드를 로그와 대조해 없는 것만 지운다. 지어낸 글 5편은 21~48%가 지워져 전체 재생성으로 넘어갔고, 정상 글은 0~10%만 지워져 나머지를 그대로 발행했다.
남은 구멍은 분명하다. 숫자나 코드가 없는 모순은 검사로 안 잡힌다. 새로 만든 글도 도입에서는 “메시지가 진짜 원인을 가리켰다”고 하고 마지막 한줄에서는 “메시지가 원인과 달랐다”고 썼는데, 문장 단위 검사로는 둘 다 통과한다.
오늘의 한줄
집계도, 테마도, 글도 결국 “무엇을 근거로 삼았는가” 에서 갈렸다 — 누적값을 턴 값으로 착각하고, 기본값을 적용된 값으로 착각하고, 형식 요구를 사실로 착각한 하루였다.