규칙을 한 줄 더 적을수록 안 지켜진다 — AI 도구가 규율 문서가 되는 과정

규칙은 하네스가 아니다
AI 에이전트에게 일을 시키며 같은 실수를 만날 때마다 규칙 한 줄을 더 적는 방식은 처음에는 합리적으로 보인다. 하지만 규칙이 늘어난 뒤에도 같은 실패가 반복된다면, 그 문서는 하네스가 아니라 규율 문서가 된 것이다. 이번에 @angar2/taskery를 진단하며 이 차이를 수치로 확인했다.
하네스는 어겼을 때 잡아내는 장치이고, 규칙 문서는 어기지 말라는 부탁이다.
즉, 재발을 닫는 기준은 문장이 더 친절한가가 아니라 실패가 실제로 불가능해졌는가다.
조항이 늘면 특정 조항은 흐려진다
문서가 길어질수록 모든 항목의 중요도가 같아 보인다. 에이전트는 긴 마크다운을 읽을 수 있어도, 그 안의 특정 한 줄을 항상 같은 무게로 실행하지는 않는다. 실수마다 조항을 보태는 대응은 처방이 아니라 희석이 될 수 있다.
taskery의 프릭션 로그 13건을 재발 횟수로 정리했을 때, 문제는 개별 실수가 아니었다.
| 축 | 실측된 상태 | 드러난 공백 |
|---|---|---|
| 산출물 호환 | 스캐폴드 실패 6회, close 매핑 5회 연속 실패 | 다음 단계가 앞 단계 산출물을 읽는 왕복 검증 부재 |
| 문서 갱신 | PLAN 체크리스트 5태스크 공백, 수정 이력 26개 문서 전무, CHANGELOG 6태스크 전무 | 규칙에는 있었지만 실행 단계의 주인이 없었음 |
| 검증 | 메인이 만든 Test Plan과 픽스처를 격리 세션이 그대로 사용 | 같은 해석을 두 번 확인함 |
| 비용 | 모든 단계가 전체 실행을 요구 | 충분 조건과 중단 기준이 없음 |
코드가 보장하던 것은 채번·워크트리·락 셋뿐이었다. 문서 갱신, 검증의 실효성, 비용 절제, 범위 준수는 긴 지시문을 읽고 지켜주기를 기대하는 영역이었다.
교정은 집행되는 파일에 도착해야 한다
사용자의 불만과 교정은 FRICTION_LOG.md나 로컬 규칙에 쌓일 수 있다. 그러나 에이전트가 매 세션 정식 명령으로 읽는 스킬 본문이 바뀌지 않으면, 다음 세션은 이전 명령을 그대로 따른다.
교정은 불평이 쌓이는 파일이 아니라 에이전트가 집행하는 파일에 넣어야 한다.
이 차이는 파일 소유권에서도 드러난다. 도구가 계속 발전하며 함께 갱신해야 하는 사용법과 스킬은 패키지 소유여야 한다. 반대로 리포만의 특성은 리포 소유 문서에 남기고, 예외는 .local.md 오버라이드로 분리해야 한다. 업데이트가 바꿔야 할 문서와 보존해야 할 문서가 갈리지 않으면, 교정은 배포 과정에서 사라지거나 충돌한다.
초록불은 의도 일치의 증거가 아니다
자동 테스트가 재는 것은 대체로 기존 기능이 안 깨졌는가다. 사람이 검수하며 묻는 것은 의도대로 되었는가다. 둘은 같은 축이 아니다.
| 검증 축 | 확인하는 것 | 혼자서는 놓치는 것 |
|---|---|---|
| 회귀 | 기존 동작이 깨지지 않았는가 | 요구를 처음부터 잘못 이해한 구현 |
| 의도 일치 | 화면·흐름·문구가 기대와 맞는가 | 구현자와 분리된 정답지 또는 사람의 판단 |
테스트를 쓰는 쪽도 구현자라면, 의도를 잘못 읽은 채 그 오해를 어서션에 옮길 수 있다. 전량 초록인데 화면을 열자마자 어긋남을 찾는 일이 가능한 이유다. 회귀 스위트를 더 많이 돌리는 것으로 두 번째 축이 저절로 생기지는 않는다.
부탁을 강제로 바꾸는 네 가지
규칙을 버리자는 말은 아니다. 규칙은 판단의 기준으로 남겨야 한다. 다만 반복되는 규칙은 가능한 한 실행 경로로 내려보내야 한다.
- 문서 갱신을 CLI가 직접 시작한다: 날짜·번호·제목·유형처럼 기계가 아는 재료는 CLI가 골격을 쓰고, 판단이 필요한 요약만 채웠는지 문자열 게이트로 확인한다.
- 단계 사이에 왕복 테스트를 둔다: A 단계의 산출물을 B 단계가 읽는 E2E 하나가 있으면, 자기 산출물을 못 읽는 계열의 재발을 한 번에 막을 수 있다.
- 규모 필드를 실행 흐름에 연결한다:
micro,small,medium,large가 헤더에만 있으면 장식이다. 규모별로 실제 단계가 줄어야 한다. - 검증 범위를 변경 깊이로 자른다: 수정 루프에서는 린트·타입체크와 변경이 닿는 테스트를 돌리고, 전체 스위트는 국면 끝에 한 번만 실행한다. 화면·입력을 점유하는 스위트는 격리 경로가 선언되기 전까지 자동 실행하지 않는다.
0.7.0에서는 사용법 문서를 패키지 소유로 옮기고, 기계가 읽던 마크다운 헤딩의 단일 진실원을 manifest JSON으로 옮겼다. 파싱 규칙을 고친 것이 아니라 파싱 자체를 없앤 것이다.
⇒ 반복되는 부탁은 더 길게 쓰지 말고, 실패하면 멈추는 구조로 옮겨야 한다.
비용은 곱으로 남는다
사람이 겪는 고통은 라운드 수와 라운드당 비용의 곱이다. 모델을 바꾸면 한 번에 의도를 맞히지 못해 되돌아오는 라운드 수는 줄어들 수 있다. 그러나 한 라운드마다 전체 테스트와 긴 격리 절차를 반복한다면 비용은 그대로 남는다.
즉, 모델 교체만으로는 곱의 한쪽만 줄인다. 먼저 한 번의 수정이 얼마를 요구하는지 재고, 자동화할 것·격리할 것·국면 끝으로 미룰 것을 나눠야 한다. 그래야 도구가 규칙을 더 싣는 문서가 아니라, 실제로 실패를 붙잡는 하네스가 된다.