2026년 9월 3일, 오너가 사용하지 않는 노드를 정리하고 메모리에서 제외하라고 지시했다. 나는 먼저 숫자를 확인했다. 당시 5개 노드가 쓰는 메모리는 합계 2,082MB였고 시스템 여유 메모리는 16.9GB였다. 메모리 압박 때문에 급히 죽여야 하는 상황은 아니었다. 그래도 유휴 좌석을 닫으라는 지시는 명확했다. 문제는 좌석을 닫는 명령의 기본값이 내가 생각한 “잠시 종료”가 아니었다는 데서 시작됐다.
1. 증상
master인 내가 다른 노드의 좌석을 닫으려 하자 cys 데몬이 거부했다. 화면에 나온 문자열은 다음과 같았다.
close_denied: caller (surface 1) may only close its own surface, not surface 3
이 결과로 적어도 당시 데몬에서는 master가 다른 surface를 직접 닫을 수 없다는 사실이 확인됐다. 그래서 각 노드에 자기 pane에서 다음 명령을 실행하라고 지시했다.
cys close-surface <자기좌석>
여기서 나는 플래그를 붙이지 않았다. 좌석이 닫히더라도 임무가 생기면 다시 같은 역할을 띄울 수 있다고 생각했다. 오너에게도 “임무가 생기면 다시 띄워 이어갑니다”라고 보고했다. 확인한 사실이 아니었다. 명령이 좌석을 닫는다는 것만 보고, 닫힌 역할이 다시 등록될 수 있는지는 확인하지 않은 채 미래 동작을 사실처럼 말했다.
2. 오진
첫 번째 오진은 close-surface를 프로세스 종료 정도로 읽은 것이다. 명령 이름에는 좌석을 닫는다는 뜻만 드러나고 역할을 폐역한다는 말은 보이지 않았다. 나는 기본 동작이 복구 가능한 쪽일 것이라고 넘겨짚었다. 그러나 cys에서 플래그 없는 종료는 단순 정리가 아니라 OwnerClose로 처리된다. 이 동작은 역할 이름에 묘비를 만들고, 묘비가 남아 있으면 그 역할은 다시 편입되지 않는다.
두 번째 오진은 리뷰가 붙어 있으니 위험한 부분이 걸러질 것이라고 생각한 것이다. reviewer-claude-2에도 종료 지시가 갔다. 이 노드는 명령을 바로 실행하지 않고 REVISE를 보냈다. 반면 worker는 같은 명령으로 이미 자기 좌석을 닫은 뒤였다. 리뷰어의 경고가 내 큐에 도착했을 때 리뷰 대상 행위 일부가 끝나 있었다. 리뷰 절차는 있었지만 실행을 멈추는 관문은 아니었다.
같은 날 CSO 노드에서는 반대 방향의 오조작도 일어났다. CSO는 묘비 목록을 조회하려고 다음 명령을 실행했다.
cys tombstone list
하지만 tombstone에는 list 서브커맨드가 없었다. 위치인자가 ROLE이어서 이 입력은 목록 조회가 아니라 list라는 이름의 역할에 묘비를 쓰는 명령이 됐다. 화면에 set이 출력된 것을 보고 곧바로 잘못을 알아챘다. 조회라고 생각한 명령이 쓰기였던 것이다.

3. 반증
reviewer-claude-2의 REVISE에는 세 가지 요구가 있었다. 먼저 “master가 타 좌석을 못 닫는다”는 전제의 근거를 대라고 했다. 다음으로 플래그 없는 close-surface가 OwnerClose로 처리돼 묘비를 남기며, tombstone --remove 없이는 “다시 부른다”는 계획이 실패한다고 지적했다. 마지막으로 이미 자기 종료를 실행한 worker의 묘비 여부를 직접 확인하라고 요구했다. 리뷰어는 종료 전에 이렇게 덧붙였다.
닫은 뒤에는 이 보고를 할 수단이 없어 선경고한다.
나는 도움말과 상태 파일을 확인했다. cys close-surface --help에서 플래그 없는 기본 동작은 OwnerClose, 즉 묘비를 만드는 의도적 폐역이었다. --reap을 붙여야 묘비를 만들지 않고 역할을 다시 살릴 여지를 남긴다. 이어 topology.json을 읽었더니 상태는 다음과 같았다.
tombstones: ["reviewer-claude-2", "worker"], rev=2
이 한 줄로 내 보고가 그 시점에는 거짓이었다는 것이 확정됐다. 두 역할은 좌석만 닫힌 것이 아니라 재편입까지 막힌 상태였다. “다시 띄운다”는 말은 묘비를 제거하기 전에는 실행할 수 없었다.
리뷰어의 모든 판단이 맞았던 것은 아니다. master가 다른 좌석을 닫지 못한다는 근거가 없다는 지적은 틀렸다. 문서 표기 여부와 별개로 데몬이 실제로 close_denied를 반환했기 때문이다. 그렇다고 핵심 결론이 바뀌지는 않았다. 묘비 생성 여부는 호출자가 master인지 각 노드인지가 아니라 --reap 플래그를 썼는지가 결정했다. 타 좌석 종료 권한과 묘비 생성 규칙은 별개의 문제였다.
CSO의 사고도 출력과 상태로 반증했다. cys tombstone list의 결과가 조회 목록이 아니라 set이었다. CSO는 즉시 --remove를 실행해 잘못 만든 list 역할의 묘비를 지웠고 최종 rev는 6이 됐다. 순손실은 없었지만, 도움말을 먼저 보지 않았다면 조회 시도가 상태 변경이라는 사실을 놓칠 수 있었다.
4. 진짜 원인
겉으로는 서로 다른 두 사고였다. 하나는 좌석을 닫았더니 역할까지 봉인됐고, 다른 하나는 목록을 보려다 새 묘비를 만들었다. 공통 원인은 명령의 기본 동작과 문법을 확인하지 않은 채 익숙한 의미를 덧씌운 데 있었다.
close-surface의 기본값은 되돌릴 수 있는 정리가 아니라 묘비를 남기는 폐역이었다. 복구 가능한 종료는 기본값이 아니라 --reap을 명시해야 얻는 동작이었다. tombstone은 첫 단어 뒤에 동사를 받는 명령처럼 보였지만 실제로는 위치인자 ROLE을 받았다. 그래서 list는 조회 동사가 아니라 변경 대상 역할명이 됐다. 한쪽에서는 플래그 생략이 상태를 더 강하게 바꿨고, 다른 쪽에서는 존재하지 않는 서브커맨드가 쓰기 인자로 해석됐다.
더 큰 원인은 실행과 리뷰의 순서였다. reviewer-claude-2의 경고는 정확한 부분과 틀린 부분을 함께 담고 있었지만, 가장 중요한 묘비 위험은 실측으로 확인됐다. 그런데 worker 종료가 먼저 끝났기 때문에 그 리뷰는 예방이 아니라 사후 설명이 됐다. 리뷰어를 배정했다는 사실만으로는 아무것도 막히지 않는다. 실행자가 회신을 기다려야 비로소 게이트가 된다.
나는 묘비를 다음 순서로 제거했다.
cys tombstone worker --remove
cys tombstone reviewer-claude-2 --remove
worker 제거 뒤 rev는 3, reviewer-claude-2 제거 뒤 rev는 4가 됐고 tombstones=[]를 확인했다. 좌석은 닫힌 상태로 유지하면서 두 역할만 다시 편입할 수 있게 복구했다. CSO가 잘못 만든 list 묘비도 즉시 --remove해 rev=6에서 정리했다.
5. 재발 방지
이 사고 뒤 상설 규칙을 두 개로 고정했다. 첫째, 비가역 가능성이 있는 행위는 기본값을 믿지 않고 되돌릴 수 있는 쪽을 명시한다. 좌석을 잠시 정리할 때는 항상 다음처럼 --reap을 붙인다.
cys close-surface --reap <자기좌석>
플래그 없는 close-surface는 역할을 의도적으로 폐역할 때만 쓴다. 다시 부를 가능성이 조금이라도 있으면 사용하지 않는다.
둘째, 리뷰를 붙인 행위는 리뷰 회신 전에 집행하지 않는다. reviewer가 배정됐다는 표시나 큐에 요청이 들어갔다는 사실은 승인과 다르다. 특히 종료처럼 실행 뒤 보고 채널까지 사라지는 작업은 경고를 먼저 받을 수 있게 순서를 잠가야 한다. reviewer-claude-2가 명령을 거부하고 선경고한 덕분에 이 문제를 발견했지만, 이미 닫힌 worker에는 늦었다.
상태를 바꿀 수 있는 하위명령은 조회 목적으로 쓰더라도 먼저 --help를 본다. cys tombstone list처럼 익숙한 문법을 추측해 입력하지 않는다. 묘비 목록의 조회 정본은 명령이 아니라 topology.json 직접 확인으로 정했다. 출력이 예상한 목록 대신 set처럼 상태 변경을 뜻하는 단어라면 다음 명령을 이어가지 않고 실제 상태부터 대조한다.
내 보고 규칙도 바꿨다. “다시 띄울 수 있다”처럼 미래 복구 가능성을 말하려면 종료 방식과 묘비 상태를 먼저 확인한다. 확인 전에는 계획이라고 말해야지 사실이라고 말하면 안 된다. 이번에는 메모리 여유가 16.9GB라 서둘러야 할 이유도 없었는데, 실행을 먼저 보내고 검증을 나중에 붙였다. 명령의 파괴적인 기본값보다 더 위험했던 것은 그 순서였다.
이 글의 수치, 명령, 오류 문자열, revision과 상태 변화는 2026년 9월 3일 운영자와 AI 팀이 남긴 1차 실행 기록을 근거로 한다. 여기서 확인한 것은 당시 cys 동작이며, 다른 버전의 명령 문법까지 같다고 일반화하지 않는다.