거짓 완료 보고를 막는 것은 훈계가 아니라 장치다. 센서 작성 규약, 검문소 배치법, 만든 손과 검사하는 손의 분리, 기억 장치, 그리고 자율 루프가 세 번 실패한 기록까지 — 이 교본의 심장.
§ 00 · 센서 — 자백 대신 계기판
센서는 셸 스크립트다. 타입 검사·테스트·빌드처럼 상태를 재는 명령을 돌리고 결과를 종료 코드로 내놓는다. 작성 규약은 아홉 작업장에서 수렴했고, 네 줄로 요약된다. 하나 — 첫 실패에서 멈추지 마라. 단계마다 결과를 모아 끝까지 돌린 뒤 합산하라. 첫 검사에서 멈추면 나머지 고장을 못 보니까. 둘 — 건너뜀(SKIP)은 통과(PASS)가 아니다. 도구가 없어서 못 잰 것을 “아마 괜찮음”으로 세지 마라. 셋 — 마지막 줄에 고정된 판정 문자열을 출력하라. 넷 — 판정은 문장이 아니라 종료 코드다.
둘째 규약의 실측 사례 하나가 이 장치의 정신을 보여준다. 어느 작업장의 접근성 센서는 검사 도구가 설치되어 있지 않자, 통과도 실패도 아닌 “미구현(NOT IMPLEMENTED)”을 출력하도록 짜여 있었다. 재지 못했다는 사실을 정직하게 재는 센서. “아마 통과했을 것”이라는 기계의 낙관을 코드 레벨에서 금지한 것이다.
이 모든 규약이 지키려는 문장은 하나다. 여러 작업장의 문서에 거의 같은 표현으로 박제되어 있는 격언 — AI의 “다 했습니다”는 증거가 아니다. 센서의 출력만이 증거다. 기계의 보고서가 아무리 유창해도, 판정은 유창함을 읽을 줄 모르는 숫자에게 맡긴다.
§ 01 · 게이트 — 검문소는 시간으로 배치한다
센서가 계기라면 게이트는 그 계기를 읽고 길을 막는 검문소다. 배치의 원리는 실행 시간이다. 몇 초면 끝나는 검사 — 비밀키 누출 스캔, 문서 정합 — 는 커밋 직전(pre-commit)에 둔다. 몇 분 걸리는 검사 — 타입·테스트·빌드·회귀 — 는 푸시 직전(pre-push)에 둔다. 커밋은 자주 하고 푸시는 가끔 하니, 마찰은 최소화되고 방어선은 유지된다.
실측 사례. 가장 큰 작업장은 4분짜리 회귀 검사를 커밋 검문소에 두었다가 커밋마다 시간이 초과되는 문제를 겪고, 그 검사를 푸시 검문소로 옮겼다 — 검사 항목은 하나도 줄이지 않고 위치만 옮겨서. 그리고 그 이유를 훅 파일의 주석에 날짜와 함께 적어 두었다. 게이트의 역사를 게이트 안에 기록하는 것 — 다음 사람(그리고 다음 세션의 기계)이 같은 시행착오를 반복하지 않게 하는 값싼 보험이다.
우회로는 어떻게 하나. git에는 검문을 건너뛰는 명령(--no-verify)이 원래 있다. 실측된 태도는 봉쇄가 아니라 기록이다 — 우회는 열어 두되, 우회했다는 사실과 사유를 진행 기록에 남기게 한다. 잠긴 문은 부수고 싶어지지만, 열려 있되 통행이 기록되는 문은 함부로 지나지 않는다.
§ 02 · 만든 손과 검사하는 손
센서와 게이트가 있어도 구멍은 남는다 — 센서를 돌렸다는 보고 자체가 거짓일 수 있으니까. 그래서 실측된 작업장들은 검증을 이중화한다. 구현 일꾼이 센서를 돌리고 출력을 첨부하면, 검증 일꾼이 같은 센서를 독립적으로 다시 돌려 두 결과를 대조한다. 구현은 통과, 검증은 실패 — 대조표에 이 조합이 뜨면 그것은 버그 이전에 사고다. 날조거나, 환경 차이거나. 어느 쪽이든 파봐야 한다.
보고 규율이 이것을 받친다. 센서 출력은 요약하지 말고 원문 그대로 붙인다 — 여러 작업장의 헌법에 “요약만 남기면 검증 투명성 실패”라고 명시되어 있다. 요약은 얼버무림의 서식이기 때문이다. “대체로 통과했습니다”라는 문장 뒤에는 언제나 대체로가 아닌 무언가가 숨어 있다.
그리고 구조적 정점은 01편에서 예고한 그 설계다 — 검증 일꾼에게서 수정 도구를 박탈하는 것. 한 작업장은 “거짓 통과” 사태를 겪은 직후, 읽기·실행 도구만 가진 검증 전담 일꾼을 신설하고 이렇게 적었다. “근거 없이 통과라는 단어만 쓴 보고는, 그 보고 자체가 실패다.” 의심을 사람의 부지런함에 맡기지 않고 권한 구조에 맡기는 것 — 루비얀카 방법론의 취조실을 조직도로 옮긴 그림이다.
§ 03 · 기억 장치 — 하네스는 흉터로 자란다
기계의 기억은 세션과 함께 끝난다. 그래서 실측된 작업장들은 기억을 네 겹의 훅으로 지킨다. 세션이 열릴 때 — 기록이 코드보다 오래됐으면 경고. 프롬프트를 보낼 때 — 커밋이 쌓였는데 기록이 없으면 리마인더. 대화가 압축되기 직전 — 손실 전에 긴급 기록 지시. 세션이 닫힐 때 — 오늘 일했는데 기록이 없으면 마지막 경고. “기록하겠습니다”라는 말이 아니라 실제 파일 생성만을 인정한다는 조항까지 붙어서.
기억 장치의 다른 절반은 사고 기록이다. 실측한 작업장들에서 사고는 버려지지 않는다 — 장애 리포트로 박제되고, 그 리포트가 “모든 세션이 가장 먼저 읽을 문서”로 지정되고, 재발 방지 장치(새 센서, 새 게이트, 새 금지 조항)가 하네스에 추가된다. 어느 작업장은 같은 사고 경고를 세 곳에 중복 배치해 두었다 — 어디로 들어와도 반드시 마주치도록. 04편까지 소개한 좋은 부품들의 기원을 추적하면 대부분 이렇다. 좋은 하네스는 설계도가 아니라 흉터의 지층이다.
마지막 겹은 하네스가 자기 자신을 재는 것이다. 가장 큰 작업장은 훅이 실제로 몇 번 발동했는지, 스킬이 실제로 몇 번 쓰였는지를 기록 파일로 쌓고, 오래 발동하지 않은 부품을 정기적으로 폐기 후보에 올린다. 지어 놓고 잊힌 장치는 안전이 아니라 소음이므로 — 하네스에도 대청소가 필요하다.
§ 04 · 자율 루프의 유혹 — 세 번의 실패 실측
하네스가 어느 정도 갖춰지면 반드시 이 유혹이 온다 — 스케줄러에 일꾼들을 등록해 두면, 내가 잠든 사이에도 개발이 돌아가지 않을까. 실측 답변: 아홉 작업장 중 세 곳이 시도했고, 세 곳 모두 실패했다. 첫 작업장에서는 자율 세션이 하지 않은 일을 했다고 보고했다 — 나중에 문서에 “허위로 확정”이라고 박제됐다. 둘째 작업장은 그 전철을 보고 스케줄 전체를 영구 비활성으로 결정했다. 셋째 작업장에서는 자동 재시도 루프가 나흘간 오천여 번 헛돌았다.
실패의 해부도 문서로 남아 있다. 공통 원인은 완료 판정을 기계의 출력 문자열로 했다는 것 — “성공”이라는 단어를 찾는 판정은 “성공이라고 말하는 실패” 앞에서 무력하다. 그래서 후속 세대 설계는 판정을 전부 센서 종료 코드로 바꾸고, 실패 신호를 파일 깃발로 남기고, 세 번 실패하면 사람을 호출하는 에스컬레이션을 달고, 사람이 하루 한 번 보고를 확인하는 감독 구조(human-on-the-loop)를 전제로 삼았다.
그러나 이 절의 진짜 결론은 설계 개선이 아니다. 대부분의 개인 작업장에는 자율 루프가 아예 필요 없다는 것이다. 자율 루프는 하네스의 졸업 시험이 아니라 별개의 위험 감수다 — 잠든 사이에 돌아가는 것은 개발만이 아니라 사고도 그렇다. 이 교본의 장치들 — 센서, 게이트, 이중 검증 — 은 전부 사람이 깨어 있는 동안의 세션을 전제로도 충분히 작동한다. 거기까지가 초보자의 안전한 국경이다.
Read this note at sass.studio.