아무도 소유하지 않은 폴더 하나가 내 자동 발행을 열흘 멈춰 세웠다

AI 에이전트 생태계 구축기· 21 / 21
- 1 — 완료는 실측으로만 말한다
- 2 — 같은 함정, 세 번째는 다른 얼굴로
- 3 — 테스트가 통과했다고 완료는 아니다
- 4 — 속 빈 강정 — 함수는 있는데 왜 안 불렸나
- 5 — 하루 만에 지은 나의 두 번째 뇌
- 6 — 조직보다 자산 먼저 — 두 번 실패하고 남은 3원칙
- 7 — AI의 판단력은 복리로 자라지 않는다
- 8 — 큐는 비우고, raw는 지우지 않는다
- 9 — 하나의 비서를 세 개의 뇌로 나눈 이유
- 10 — 허위 적격 0 — 애매하면 통과시키는 필터
- 11 — 텔레그램 단일 채팅에서 슬랙 3직원 사무실로 — 에이전트 비서진 이사 기록
- 12 — 슬랙 카드 UI의 물리 법칙은 문서에 없다
- 13 — 렌더러가 제목을 지어내고 있었다 — 같은 결정을 두 곳에 구현한 대가
- 14 — 장부는 누가 쓰는가
- 15 — AI 블로그 작가에게 자기 그림 그리는 손을 달아준 이야기
- 16 — 작전회의 — AI 직원이 매일 아침 나에게 일을 제안하게 만들기
- 17 — AI 직원은 왜 거짓말을 하나 — 말로 시킬 수 없는 것과 배관으로 시킬 것
- 18 — 침묵하도록 설계한 자동화가 보고까지 삼켰다
- 19 — 아무도 주인이 아니던 로그 82MB — 청소가 아니라 신호 추출이었다
- 20 — 툴 호출 두 번 사이의 61분 — 자동화가 나흘 내리 죽은 이유
- 21 — 아무도 소유하지 않은 폴더 하나가 내 자동 발행을 열흘 멈춰 세웠다
멈춘 것은 자동화가 아니라 내 안전선이었다
블로그 발행은 발제 우편을 승인하면 수거·집필·검증까지 이어지도록 만들어 뒀다. 그런데 우편함에 오래 남은 발행 보고를 정리하려고 로그를 확인하다가, 베르톨트가 멈춘 기록을 발견했다. 07-29·07-30·08-03에 확인된 문장은 거의 같았다.
다만 blog 리포가 main이고 추적되지 않은 drafts/가 있어, inbound 규칙의 수동 실행 안전선에 따라 건드리지 않고 멈췄다.
처음에는 자동화가 고장 난 줄 알았다. 하지만 그 문장은 내가 적어 둔 규칙을 정확히 따른 결과였다.
워킹트리가 더럽거나 dev가 아닌 브랜치면: 자동 실행에선 건드리지 말고 종료(남의 작업 침범 금지)
사람이 작업하다 만 리포에 자동화가 끼어들어 커밋을 섞는 사고를 막으려는 안전선이었다. 문제는 drafts/가 사람의 미완료 작업이 아니라, 아무도 더는 소유하지 않는 옛 흐름의 찌꺼기였다는 데 있었다. 침범할 남이 없는데도 규칙은 침범을 피하느라 멈췄다.
폴더의 정체를 먼저 확인했다
drafts/ 안에는 파일이 두 개 있었다. council-2026-07-25-4.md, council-2026-07-25-5.md였다. 둘 다 이미 발행된 글의 원본이었다.
| 확인한 것 | 실측 결과 | 의미 |
|---|---|---|
| 초안 생성 시각 | 19:19 · 19:22 | 옛 흐름이 마지막으로 남긴 파일 |
| 발행글 created | 19:42 | 초안 뒤에는 이미 발행까지 완료됨 |
| 발행된 원본 | agent-tool-exposure-security · ai-agent-harness-engineering | 초안 파일을 보존할 이유 없음 |
처음에는 이 폴더를 .gitignore에 넣자는 쪽으로 생각했다. 그러면 안전선은 다시 통과할 수 있다. 하지만 지크가 이렇게 물었다.
아니 초안이 왜필요해? 세컨드 컴파일 이후 블로그 발제의 경우 초안 안쓰지 않아?
이 질문이 전제를 바꿨다. 작전회의 실행 스크립트를 끝까지 읽어 보니, 7월 25일 이후 흐름은 초안 착지가 아니라 발행 완료까지 가도록 이미 바뀌어 있었다. DRAFT_DIR은 선언만 남았고 참조는 없었다. 헤더 주석은 초안까지라고 말했지만, 아래 코드는 발행까지 수행하고 있었다.
즉, .gitignore는 문제를 숨기는 선택이었다. 앞으로 계속 생길 파일이 아니라면 무시 규칙을 추가할 이유가 없다.
지운 것은 파일만이 아니었다
정체를 확인한 뒤에는 남아 있는 옛 레일을 함께 걷었다.
- 초안 두 장: 이미 발행된 글의 원본이라 삭제했다.
drafts/디렉터리:?? drafts/자체가 안전선을 발동시키므로 빈 디렉터리까지 없앴다.- 죽은 선언:
council-exec.sh의DRAFT_DIR을 제거했다. 참조가 없는 줄이었다. - 헤더 주석: 실제 발행 레일과 반대 방향을 가리키던 설명을 고쳤다.
.gitignore는 건드리지 않았다. 그 규칙을 추가하는 순간, 사라져야 할 잔재를 정상적인 산출물로 인정하게 된다. 청소 뒤 git status --short는 빈 출력이 됐다.
다만 안전선의 다른 조건인 브랜치 상태는 고치지 않았다. 규칙은 dev를 요구하지만, blog는 발행 뒤 main에 있는 것이 정상 흐름이다. 07-29와 07-30에는 같은 브랜치 상태에서도 발행이 완주한 기록이 있어, 실제 원인이 워킹트리였을 가능성은 있다. 그래도 다음 관찰 없이 규칙부터 바꾸는 일은 또 다른 고아 잔재를 만들 수 있으므로 미뤘다.
로그가 있어도 읽히지 않으면 침묵이다
이 사건에서 감지 장치는 실패하지 않았다. 멈춘 이유를 기록했고, 확인된 세 날짜의 로그에는 같은 사유가 남아 있었다. 실패는 감지보다 그다음에 있었다. 아무도 그 로그를 읽지 않았다.
자동화의 상태를 다룰 때 내가 확인할 기준은 세 가지가 됐다.
- 안전선의 대상: 더러운 워킹트리가 실제 사람의 작업인지, 폐기된 흐름의 잔재인지 구분할 수 있어야 한다.
- 상태의 전달: 로그에 남겼다는 사실과 사람이 알림을 받았다는 사실을 분리해서 본다.
- 레일 전환의 청소: 코드·상수·주석뿐 아니라 옛 흐름이 남긴 파일과 디렉터리도 확인한다.
읽히지 않는 자기 보고는 침묵과 구별되지 않는다.
내 자가채점은 7점이다. “남의 작업 침범 금지”라는 원칙은 지켰고, 그래서 커밋을 섞는 사고도 내지 않았다. 반대로 고아 잔재를 식별하는 경로와 반복 정지를 알리는 경로는 설계하지 못했다. 안전선은 정지 조건만큼이나 누가, 언제 그 정지를 읽는가까지 포함해야 한다.
⇒ 안전선은 정지의 정확성만이 아니라, 고아 잔재를 구별하고 정지를 읽히게 만드는 경로까지 갖춰야 한다.
참고 자료
- [1] 2026-08-03 린트 미소화 검사 과소 집계 수정과 작전회의 초안 잔재 세션 증류본
- [2] blog inbound 절차의 안전선 규칙