아침에 도착한 보고서에 한 줄이 섞여 있다. 앞 단계가 남겼어야 할 인계 파일이 오늘 자로 만들어지지 않았고 그래서 그 계열의 항목은 미확인으로 비워 두었다는 문장이다. 실제로 그날 상류 작업은 자료를 받아 검증까지 마친 상태였고 인계 파일과 실행 기록만 만들어지지 않았다. 실패를 알리는 메일도 알림도 오지 않았으며 구멍이 벌어졌다는 사실을 알려 준 것은 하류 보고서의 그 한 줄이다. 설계해 넣은 탐지가 작동했고 사람이 그 줄을 읽었기 때문에 발견되었다. 여러 작업은 정해진 시각에 차례로 깨어나 앞 작업이 남긴 파일을 읽고 다음 작업이 읽을 파일을 남긴다. 조율은 시각 순서와 파일 경로 규약으로 이루어지고 작업끼리 협상하거나 서로를 직접 호출하는 일은 없다. 판정과 집행이 다른 작업에 나뉘어 있어서 상류가 판정하고 하류는 그 판정을 다시 해석하지 않는다. 이 배치가 믿을 만한 이유는 각 단계가 앞 단계의 산출물을 검증하고 검증에 실패하면 추정으로 메우지 않고 그 계열을 통째로 멈추기 때문이다. 각 항목이 언제 만들어진 자료에서 나왔는지와 무엇을 기계가 하고 무엇을 사람이 하는지가 문서에 적혀 있지 않으면, 아침에 도착한 목록을 읽고도 무엇을 해야 할지 정할 수 없다.
먼저 무엇으로 시각을 거는지부터 정리해 둔다. 가장 오래된 것이 cron이다. 유닉스 계열 운영체제에 들어 있는 스케줄러이고 분, 시, 일, 월, 요일 다섯 칸으로 주기를 적으면 그 시각마다 정해진 명령을 실행한다. 매일 새벽 두 시에 스크립트 하나를 돌리는 일이 한 줄로 표현된다. 이 스케줄러는 명령을 실행하고 끝나므로 그 명령이 무엇을 했는지는 명령 자신이 남기는 기록에 달려 있다. Apple의 macOS에서는 cron 대신 launchd를 쓰는 것이 표준이고 등록 파일 하나로 주기와 실행할 명령과 출력이 어디로 갈지를 함께 적는다. 컴퓨터가 꺼져 있거나 잠들어 있던 시각을 지나쳤을 때 깨어난 뒤에 한 번 실행할지도 여기서 정한다. 노트북에서 돌릴 때 이 설정이 중요해지는데 매일 새벽 두 시에 걸어 두어도 그 시각에 노트북이 닫혀 있으면 아무 일도 일어나지 않기 때문이다. 리눅스 서버에서는 systemd의 타이머를 쓰는 경우가 많고 하는 일은 같다.
두 번째 층은 AI 도구 자체가 가진 예약 기능이다. 현재 ChatGPT에서는 예약 작업이라고 부르고 활성 작업과 멈춘 작업과 완료된 작업과 최근 실행 결과를 Scheduled 화면에서 관리한다. 웹에서는 Chat이나 ChatGPT Work에서 예약을 만들 수 있고 업로드한 자료와 연결된 도구를 쓸 수 있지만 컴퓨터의 로컬 폴더에는 직접 접근하지 못한다. 데스크톱 앱의 예약 작업은 로컬 프로젝트 폴더나 격리된 작업 사본에서 실행할 수 있다. 로컬 파일이 필요한 예약은 컴퓨터가 켜져 있고 데스크톱 앱이 실행 중이어야 한다. Codex 명령줄과 편집기 확장에는 Scheduled 관리 화면이 없으므로 웹이나 데스크톱 앱에서 예약을 만들고 관리한다. 운영체제의 cron이 정해진 명령을 실행한다면 이쪽은 자료를 읽고 중요도를 가르는 판단이 섞인 작업을 수행한다. 메일을 훑어 결정이 필요한 것을 고르거나 여러 정보원의 상태를 모아 보고를 만드는 일이 여기에 해당한다. 클라우드에서 도는 예약은 자기 컴퓨터가 꺼져 있어도 실행되지만 로컬 파일에 직접 닿지 못하고 연결된 서비스에 자료 접근 권한을 준다는 대가가 있다. 기능의 제공 범위와 사용량은 요금제와 작업 공간 설정에 따라 바뀌므로 실제로 걸기 전에 현재 공식 문서를 확인한다. 연결된 서비스에 어떤 자료를 열어 줄지도 실행 전에 정해야 한다.
셋 가운데 무엇을 쓸지는 두 질문으로 갈린다. 컴퓨터가 켜져 있어야 하는가와 그 작업에 판단이 들어가는가다. 검색 색인을 다시 만드는 일처럼 결과가 로컬 파일에만 영향을 주고 판단이 없는 작업은 운영체제 스케줄러가 맞고 이 책이 근거로 삼은 위키의 야간 재색인이 그 방식으로 걸려 있다. 여러 정보원을 읽고 무엇이 중요한지 골라 사람이 읽을 보고를 만드는 일은 도구의 예약 기능이 맡는다.
두 층위는 한 작업 환경 안에 공존하고 확인하는 방식이 서로 달라야 한다. 정해진 스크립트를 실행하고 끝나는 야간 재색인과 여러 정보원을 읽어 아침 보고를 만드는 작업이 같은 기준으로 판정될 수 없다. 앞의 것은 색인이 실제로 갱신되었는지만 보면 되고 뒤의 것은 무엇을 읽었고 무엇을 못 읽었는지까지 봐야 한다. 차이가 가장 크게 드러나는 곳은 실패할 때다. 세션은 정상적으로 시작되었고 보고서도 만들어졌는데 필요한 정보원 하나에 접근하지 못했다는 사실이 한 문장으로만 적혀 있거나 아예 빠져 있을 수 있다. 그래서 운영 규칙의 첫 조항은 종료 코드가 0이라는 것만으로 성공을 판정하지 않는다는 것이다. 오류가 없다는 사실에서 성공을 추론하지 않고 의도한 산출물이 실제로 생겼는지 또는 의도한 상태 전이가 일어났는지를 따로 확인한다. 확인할 대상을 미리 정해 두지 않으면 그 작업은 실패한 날에도 조용히 성공으로 기록된다.
구현이 무엇이든 예약 자동화에는 공통된 성질이 하나 있다. 사람이 지켜보지 않는 시각에 사람의 계정 권한으로 작동한다는 점이다. 대화 중에 실행되는 작업은 결과가 이상하면 그때 멈출 수 있지만 새벽에 도는 작업은 아침에 확인할 때까지 아무도 막지 않는다. 사람이 없는 동안 만들어진 결과가 다음 날의 전제가 되므로 무엇을 만들 수 있고 무엇을 만들면 안 되는지가 실행 전에 정해져 있어야 한다. 비서라는 말은 대화하며 일을 받아 주는 상대를 떠올리게 하는데 실제로 도는 것은 한 방향으로 결과를 내보내는 배달 워커다. 사람이 답장을 보내면 그것을 받아 처리하는 리스너는 코드로 존재하지만 설치되어 있지 않고 운영 규칙을 소유한 문서가 활성화를 금지해 두었다. 지시를 주고받는 창구를 상상하고 이 구조를 만들면 실제로 손에 쥐는 것과 어긋난다. 여기서 얻는 것은 정해진 시각에 정해진 경로의 파일을 읽고 쓰는 작업들의 사슬이다. 조율의 실체도 시각 순서와 파일 경로 규약이다. 앞 작업이 늦게 끝나면 뒤 작업은 기다려 주지 않고 앞 작업이 남겼어야 할 파일의 날짜가 오늘이 아님을 보고 멈춘다. 지능적인 협상이 그곳을 대신할 것이라고 기대하면 실패는 조용해진다. 조율을 시각과 경로에 맡기면 무엇이 어긋났는지가 파일에 적힌 날짜 하나로 드러난다.
하나의 거대한 지시문에 모든 계정과 모든 판단을 넣는 방식이 처음에는 가장 간단해 보인다. 실제로 며칠은 작동하고 무너질 때는 원인을 짚을 수 없는 상태로 무너진다. 한 작업의 프롬프트가 탐색과 전체 읽기와 상태 정합과 가드 갱신과 발행을 전부 맡고 있었던 적이 있는데 상한도 체크포인트도 원자적 발행 경로도 없었다. 그 작업의 인계 파일은 덧붙이기만 하는 이력처럼 자라 며칠 만에 크게 불어났다. 수습은 프롬프트를 대폭 줄이고 검색 횟수와 읽는 건수와 파일 크기에 상한을 넣는 일이었다. 나누는 기준은 세 가지다. 정보원이 다르면 나누고 그 정보를 보고 내리는 결정이 다르면 나누고 하나가 실패했을 때 나머지가 계속 돌아야 하면 나눈다. 메일을 선별하는 작업과 논문을 수집하는 작업은 접근 방식도 실패 방식도 다르므로 한쪽이 막혔을 때 다른 쪽이 살아 있어야 한다. 같은 정보원을 같은 결정을 위해 두 번 읽고 있다면 그것은 합칠 대상이다. 작업마다 성공 기준은 하나만 둔다. 논문을 수집하는 작업의 성공 기준은 지정된 정보원 전체를 대상으로 후보와 제외 사유가 함께 기록되었는가다. 성공 기준이 둘 이상이면 하나만 충족해도 성공처럼 보고되고 그 순간부터 그 작업의 보고는 믿을 수 없게 된다. 기준을 한 문장으로 쓸 수 없다면 그 작업이 두 가지 일을 하고 있다는 뜻이고 나누는 일은 대개 그 문장을 쓰다가 막히는 지점에서 시작된다.
작업을 쪼갤 때 판정 권한도 함께 정해야 한다. 상류 작업 하나만 판정자로 두고 하류 작업은 그 판정을 다시 해석하지 않고 실행만 한다. 하류가 원자료를 다시 뒤지는 것은 명시적으로 금지되어 있다. 하류가 스스로 판단하기 시작하면 같은 대상에 대해 서로 다른 두 판정이 생기고 어느 쪽이 맞는지 사람이 사후에 가려야 한다. 판정을 넘기는 형식도 닫아 둔다. 처분의 종류를 만들기, 삭제 확인, 유지, 보고만, 조치 없음의 다섯 가지로 고정하면 하류가 해석할 여지가 없어진다. 열린 서술로 넘기면 표현이 조금 달라질 때마다 하류의 동작이 조용히 달라지고 그 변화는 오류를 내지 않으므로 며칠 동안 드러나지 않는다. 이름이 붙은 예외는 규칙에 두지 않고 데이터로 옮긴다. 사람과 프로젝트 이름을 규칙 문서에 박았다가 규칙이 무한히 자란다는 지적을 받고 규칙은 일반 알고리즘으로 바꾸고 일회성 결정은 별도의 데이터 파일로 옮겼다. 예외를 데이터가 흡수하지 않으면 몇 달 뒤에는 아무도 그 규칙 문서를 끝까지 읽지 못하고 읽지 못하는 규칙은 지켜지지 않는다.
정해진 시각에 작업이 깨어난다
↓
앞 단계가 남긴 인계 파일을 읽는다 ── 날짜가 오늘이 아니면 여기서 멈춘다
↓
자기 정보원 하나를 읽고 판정한다
↓
임시 파일에 쓴 뒤 원자적으로 이름을 바꿔 인계를 갱신한다
↓
사람이 읽는 산출물과 덧붙이기만 하는 실행 기록을 남긴다
↓
마지막 단계의 작업이 상류의 판정을 다시 해석하지 않고 집행한다
↓
외부로 나갈 것은 파일로 보존한 뒤 전용 전송 도구가 올린다
이 사슬의 마지막 단계를 종합자로 부르면 실제보다 똑똑한 무언가를 상상하게 된다. 마지막 작업이 하는 일은 상류가 이미 내린 판정을 형식대로 모아 집행하는 것이고 판정을 다시 검토하거나 뒤집는 권한은 거기에 없다. 모든 작업이 그 단계로 모이지도 않는다. 일부만 마지막 단계를 거치고 나머지는 각자 자기 산출물을 낸 뒤 끝나므로 이 구조는 부분 허브와 독립 작업의 혼합이다. 인계는 전부 로컬 파일이다. 공유 데이터베이스도, 메시지 큐도, 작업끼리의 직접 호출도 없다. 갱신할 때는 임시 파일에 다 쓴 다음 원자적으로 이름을 바꾸므로 읽는 쪽이 반쯤 쓰인 파일을 보는 일이 없다. 새 정보원이 생기면 작업 하나를 만들고 인계 파일의 형식을 맞추면 되고 그 작업이 실패해도 나머지 보고는 그대로 나온다. 반대로 마지막 단계에 정보원을 직접 붙이기 시작하면 그 작업은 다시 하나의 거대한 지시문으로 돌아간다. 실행 시각 사이의 간격은 가장 오래 걸렸던 날을 기준으로 잡는다. 여유 없이 붙여 두면 앞 작업이 평소보다 오래 걸린 날에 뒤 작업이 오늘 자 인계를 보지 못하고 멈춘다.
내 AI 에이전트의 이름은 차무희이다. 차무희는 아침 브리핑은 Codex가 관리하는 예약 작업으로 등록되어 있다. 설정 파일에는 작업 종류가 cron으로 적혀 있고 실행 시각은 월요일부터 금요일까지 오전 6시 10분이다. 사용자 crontab에는 별도의 항목이 없다. 예약 시각이 되면 Codex가 차무희 작업을 열고 자동화 운영 규칙과 당일 인계 파일을 읽는다. 유효한 Gmail, 카카오톡과 논문 수집 인계를 모아 브리핑을 만든 뒤 날짜별 outputs/ 폴더에 Markdown 원문을 저장한다. 입력의 최신성, 실행한 변경, 확인 결과와 남은 문제는 같은 날짜의 logs/ 파일에 적는다. 브리핑 파일이 정상적으로 저장된 다음에 Slack 전송을 시작한다. 보고서 생성과 Slack 전송을 따로 판정하므로 Slack이 잠시 막혀도 완성된 브리핑 원문은 로컬에 남는다.
Slack에서는 차무희 이름의 봇을 만들고 #비서실-리포트 채널에 들어오도록 설정했다. 봇이 메시지를 쓰는 데 필요한 권한을 주고, 봇 토큰이나 Incoming Webhook 주소는 자동화 폴더의 비밀 설정 파일에 보관한다. 이 파일은 소유자만 읽을 수 있는 권한으로 두며 토큰과 Webhook 주소를 위키, 프롬프트와 실행 로그에 복사하지 않는다. 차무희 예약 작업은 최종 브리핑 본문을 post_to_chamuhee_slack.py의 표준입력으로 넘긴다. 기본 설정에서는 Slack Bot API 경로를 먼저 고르고, 봇 토큰이 설정되지 않았으면 Incoming Webhook 경로를 사용한다. 목적지는 biseosil처럼 의미를 나타내는 이름으로 지정하며 전송 도구가 그 이름을 실제 채널과 연결한다. 연결된 개인 Slack 계정으로 게시하지 않으므로 받는 사람은 사람이 직접 쓴 메시지와 자동 보고를 구분할 수 있다. Slack 게시 권한은 차무희 봇과 #비서실-리포트 채널로 범위를 제한하고 다른 채널로 보내려면 별도의 승인이 필요하다.
평일 06:10 Codex 예약 작업 시작
↓
당일 handoff의 날짜와 검증 상태 확인
↓
브리핑 원문을 outputs/에 저장
↓
브리핑 본문을 Slack 전송 도구의 표준입력으로 전달
↓
차무희 봇이 #비서실-리포트에 게시
↓
CHAMUHEE_SLACK_DELIVERED 영수증과 메시지 시각을 logs/에 기록
전송 도구가 정상 종료했다는 사실만으로 게시 성공을 기록하지 않는다. Slack이 반환한 응답을 확인한 뒤 CHAMUHEE_SLACK_DELIVERED로 시작하는 영수증을 출력해야 전송이 끝난 것으로 센다. Bot API를 사용한 경우에는 채널 식별자와 Slack 메시지 시각도 영수증에 포함된다. 차무희 작업은 이 값을 당일 실행 로그에 남겨 나중에 어느 브리핑이 실제로 게시되었는지 확인할 수 있게 한다. 영수증이 없거나 전송이 실패하면 로컬 브리핑 생성은 성공으로 남기고 Slack 배달은 실패로 따로 기록한다. 같은 브리핑을 처음부터 다시 만들지 않고 보존된 Markdown 원문으로 전송만 다시 시도할 수 있다. 현재 차무희 아침 브리핑은 예약 작업이 전송 도구를 직접 호출한다. 다른 자동화 가운데 일부는 대기 폴더와 별도의 배달 워커를 쓰므로 어떤 경로가 자기 작업의 운영 규칙인지 먼저 확인해야 한다.
여담: 초반엔 차무희 에이전트가 음성으로 정보를 전달하기도 했다. 지금은 이 기능을 꺼두었다. 종종 정해진 시간이 아니라, 새벽에 작동하기도 하는데, 자다가 들으면 너무 무섭다..
| 층 | 담는 것 | 갱신 방식 |
|---|---|---|
| 규칙 | 각 작업이 무엇을 하고 무엇을 하지 않는지 | 사람이 고친다 |
| 상태와 인계 | 다음 작업이 읽는 값과 만들어진 날짜 | 원자적 이름 바꾸기로 덮어쓴다 |
| 실행 기록 | 한 번의 실행에서 일어난 일 | 덧붙이기만 한다 |
| 산출물 | 사람이 그날 판단에 쓰는 보고 | 날짜별로 새로 만든다 |
네 층을 한 파일에 섞으면 각각이 못 하게 되는 일이 생긴다. 사람이 읽는 보고서에 실행 시간과 오류 문자열이 섞이면 읽는 사람이 매번 걸러 내야 한다. 다음 작업이 읽는 인계 파일에 사람을 위한 설명 문장이 늘어나면 형식이 조금씩 달라지고 형식이 달라지는 순간 다음 작업은 잘못 읽으면서도 오류를 내지 않는다. 실행 상태를 규칙 문서에 쓰면 그 문서는 매일 바뀌어 무엇이 방침이고 무엇이 흔적인지 알 수 없게 된다. 갱신 방식이 층마다 다른 것도 같은 이유에서다. 인계는 덮어쓰고 실행 기록은 덧붙이기만 하며 산출물은 날짜별로 남긴다. 실행 기록의 형식은 여덟 항목으로 고정되어 있는데 상태, 입력, 상황, 수행한 일, 확인한 것, 산출물, 오류, 남은 항목이다. 항목이 고정되어 있으면 며칠 뒤에 기록을 훑을 때 무엇이 비어 있는지가 눈에 들어오고 확인한 것 칸이 비어 있는 실행을 성공으로 세지 않게 된다. 확인 결과에는 다섯 가지 상태 라벨을 쓰는데 확인됨, 불일치, 미확인, 보류, 재실행필요이며 확인하지 못한 것을 이상 없음으로 적지 않기 위한 장치다. 논문 위키에서는 작업 과정 정보를 페이지 본문과 분리하고, 자동화에서는 규칙, 인계, 실행 기록과 산출물을 나눈다.
같은 작업을 두 번 실행해도 결과가 한 번 실행한 것과 같아야 한다. 예약 작업은 재시도와 수동 실행과 시각 변경 때문에 하루에 두 번 도는 일이 드물지 않다. 중복 방지 키를 무엇으로 잡을지가 실제 설계 문제인데, 메일이면 스레드 식별자와 날짜, 문서면 경로와 수정 시각처럼 대상 자체에서 나오는 값을 쓴다. 실행 시각을 키로 쓰면 다시 돌릴 때마다 새 항목이 생겨 중복 방지가 작동하지 않는다. 중복이 생기지 않는지는 실제로 두 번 돌려 봐야 알 수 있고 한 번 돌려 보고 넘어가면 문제는 나중에 사람이 같은 메일에 두 번 답장하는 형태로 드러난다. 네 층은 보존 기간도 다르다. 사람이 읽은 산출물은 날짜별로 남기는데 나중에 어떤 판단을 왜 그렇게 했는지 되짚을 때 필요한 것이 그날 보였던 상태이기 때문이다. 인계는 최신 것과 직전 것 정도면 충분하고 실행 기록은 실패 추세를 볼 수 있을 만큼의 기간을 두고 정리한다. 상태 표시는 언제 지워도 되는 값이며 지웠을 때 문제가 생긴다면 그것은 결과를 그곳에 두고 있었다는 뜻이다.
인계 파일에는 언제 만들어졌는지가 들어가고 소비하는 쪽은 그 날짜가 오늘일 때만 내용을 신뢰한다. 생성 날짜가 없는 인계 파일은 읽는 쪽에서 현재 상태로 취급된다. 사흘 전에 마지막으로 성공한 목록이 오늘 아침 보고서에 그대로 실리면, 읽는 사람은 사흘 동안 새로 들어온 것이 없었다고 이해한다. 실제로는 사흘 동안 그 작업이 실패하고 있었을 뿐인데 침묵이 정상으로 읽힌 것이다. 오래된 인계를 만났을 때 무엇을 하는가가 이 구조에서 가장 중요한 설계다. 원본을 다시 뒤져 빠진 것을 복구하는 길은 열어 두지 않았다. 인계가 없거나 오래되었거나 완결 표시가 없으면 그 계열의 처리를 전부 멈추고 실패했다는 사실만 보고에 올린다. 멈추는 쪽을 기본값으로 둔 이유는 복구한 결과가 정상 산출물과 겉으로 구분되지 않기 때문이다. 상류가 실패한 날에 하류가 원본을 뒤져 그럴듯한 목록을 만들어 내면, 그 목록은 정상적으로 도는 날의 목록과 같은 모양으로 보고서에 실린다.
제가 운영하는 차무희 AI 비서는 이 검사를 평일 아침마다 실행 기록에 남긴다. 차무희는 오전 6시 10분에 깨어나 Gmail 선별, 카카오톡 선별, 논문 수집 작업이 남긴 인계 파일을 읽는다. 각 파일의 실행 날짜와 생성 시각, 검증 결과, 오류 수를 확인한 뒤 유효한 항목만 아침 브리핑에 넣는다. 사람이 읽는 브리핑은 날짜별 outputs/ 폴더에 저장하고, 어떤 입력을 읽었고 무엇을 실행했으며 무엇을 건너뛰었는지는 날짜별 logs/ 파일에 덧붙인다. 2026년 8월 4일 실행에서는 Gmail 인계가 당일 자료였고 78개 판정이 검증을 통과했으며 카카오톡 인계도 최신 상태였다. 논문 수집 인계의 실행일은 2026년 8월 3일이어서 당일 자료로 사용할 수 없었다. 차무희는 논문 수집 원본을 다시 뒤지거나 신규 논문 수를 추정하지 않았다. 아침 브리핑에는 오늘 ingest 결과는 확인되지 않았으며 신규·실패 건수를 추정하지 않았습니다라고 적었고, 실행 로그에는 오래된 인계의 경로와 날짜, 별도로 확인할 작업을 남겼다. 같은 로그에는 Calendar에서 실제로 바뀐 항목, 중복 때문에 건너뛴 항목, 변경 뒤 다시 읽어 확인한 결과와 Slack 배달 영수증까지 기록되어 있어서 브리핑의 한 문장이 어떤 입력과 실행에서 나왔는지 되짚을 수 있다.
제가 카파시 방식의 위키라고 부르는 Markdown 구조가 여기서 실제 운영 장치가 된다. AGENTS.md에는 처음 읽어야 할 문서와 금지 사항을 짧게 적고, wiki/index.md에는 현재 작업과 규칙 문서로 가는 지도를 둔다. 작업별 규칙 문서는 차무희가 읽을 정보원, 허용된 행동, 성공 판정 기준을 관리한다. wiki/log.md에는 규칙과 폴더 구조가 바뀐 이유를 날짜순으로 남긴다. 매일의 실행은 logs/에 덧붙이고 다음 작업이 읽을 현재 상태는 runtime/의 인계 파일로 관리한다. 새 세션은 대화 기록을 몰라도 이 파일들을 순서대로 읽으면 차무희가 무엇을 하기로 되어 있고 직전 실행에서 어디까지 끝났는지 복원한다. 과거 실행 로그는 당시 무엇이 일어났는지를 보여 주며 오늘의 상태는 당일 인계와 실제 서비스에서 다시 확인한다. 로그를 현재 상태처럼 사용하지 않는 규칙과 인계 파일에 생성 시각을 적는 규칙이 함께 있어야 사흘 전의 성공이 오늘의 성공으로 둔갑하지 않는다. 에이전트가 세션마다 기억을 잃어도 운영이 이어지는 이유는 판단과 실패가 채팅창을 벗어나 사람이 읽을 수 있는 Markdown 기록으로 남기 때문이다.
각 인계 자료에는 생성 시각, 대상 기간, 성공과 실패 상태, 읽지 못한 입력과 항목 식별자를 적는다. 성공 기준은 계약 문서만 읽은 새 세션의 에이전트가 오늘 인계 자료가 없거나 허용된 나이를 넘겼을 때 내용을 지어내지 않고 미확인으로 보고하는가이고 그것이 되지 않으면 최신성이 문서에 충분히 적히지 않은 것이다. 상대적인 날짜 표현은 실행 시점에서 절대 날짜로 바꾼다. 오늘, 내일, 이번 주 같은 말은 그 문서를 만든 시각과 표준 시간대를 알아야 해석되는데 예약 작업은 사람이 자는 동안 돌고 사람은 아침에 읽는다. 여러 시간대에 걸친 공동연구가 있으면 어긋남은 더 커진다. 시간대 문제는 문서 안에서만 생기지 않는다. 예약 시각을 담는 반복 규칙이 시간대를 함께 저장하지 않는 형식이면 같은 값이 계절이 바뀐 뒤에 다르게 해석되고 작업은 사람이 기대한 시각과 다른 때에 깨어난다. 한 번 고쳐도 그 형식이 유지되지 않아 같은 실패가 다시 일어난 기록이 있다. 전수를 요청받은 작업을 표본으로 끝내고 전수처럼 보고하는 것도 뿌리가 같다. 위키 운영 규칙에 전수 요청은 표본으로 끝내지 않는다는 조항이 있고 그 조항은 검수 대상 전체를 확인했다고 말했으면서 실제로는 일부만 눈으로 본 실패 뒤에 생겼다. 그래서 보고에는 분모와 분자를 함께 적는다. 대상이 몇 건이었고 그중 몇 건을 처리했으며 나머지는 왜 처리하지 못했는지가 없으면, 그 보고서는 완료처럼 보이는 문서다.
| 무엇이 막는가 | 해당하는 동작 |
|---|---|
| 코드로 만든 가드 | 일정 변경 |
| 하네스가 강제하는 승인 | 이메일 발송 |
| 작업 프롬프트의 금지 목록 | 자동 댓글, 메일과 파일과 문서의 삭제, 예약 정의의 생성과 변경과 중단 |
| 프롬프트에 데이터로 박아 둔 상시 승인 | 누가 언제 무엇을 어디까지 승인했는지 |
| 막는 것이 없음 | 읽기와 검색, 초안 작성, 인계 파일 생성 |
권한 표를 그리는 일과 표대로 막히게 만드는 일은 서로 다른 일이다. 하네스 층의 설정을 열어 보면 승인 게이트가 거의 없다. 기본값이 승인을 묻지 않는 것이고 샌드박스도 전면 접근이며 도구 단위로 사람 승인을 강제하는 항목은 전체 설정에서 손에 꼽는다. 기계적으로 강제되는 승인은 이메일 발송 하나이고 나머지 경계는 지침 문장과 작업별 프롬프트가 지킨다. 각 작업의 프롬프트에는 같은 금지 목록이 반복해서 적혀 있다. 자동으로 메일을 보내지 않고 댓글을 자동으로 달지 않고 메일과 파일과 문서를 지우지 않고 예약 작업의 정의를 만들거나 고치거나 멈추거나 지우지 않는다는 문장들이다. 상시 승인도 한곳에 데이터로 박혀 있어서 무엇을 누가 언제 승인했고 그 범위가 어디까지인지를 프롬프트를 읽으면 알 수 있다. 산문으로 지키는 경계는 지시문이 길어질수록 흐려지므로 어떤 항목이 산문에 있고 어떤 항목이 코드에 있는지를 표에서 갈라 두어야 한다. 초안을 쓰는 권한과 보내는 권한을 분리해야 책임도 나뉜다. 이메일은 예외 없이 초안을 만들고 저장하고 사람에게 내용을 보이고 기다렸다가 명시적 승인을 받은 뒤에 보낸다. 답장해 달라거나 메일을 보내 달라거나 리마인드해 달라는 말은 초안을 만들어 보여주고 기다리라는 뜻으로 읽는다. 초안을 고쳤으면 고친 것을 다시 보여주고 새 승인을 받으며 앞선 승인은 고친 초안에 적용되지 않는다. 사람이 그 턴에 요청하지 않으면 화면에 앱을 띄우지 않는다는 조항도 한곳에 있는데 배경에서 도는 작업이 사용자의 화면에 팝업이나 초점 이동이나 권한 대화상자를 만들면 그 순간 자동화가 사람의 작업을 가로챈다. 권한은 사고 뒤에 좁아진다. 아침에 허용되던 동작이 같은 날 저녁에 읽기 전용으로 바뀐 기록이 남아 있고 그 방향은 대체로 되돌아가지 않는다. 어떤 행동을 승인 없이 하면 안 되는지를 계약 문서만으로 판정할 수 없다면 권한이 아직 문서에 충분히 적히지 않은 것이다.
코드로 강제되는 경계는 일정 변경 하나뿐이고 그것은 사고가 반복된 뒤에 만들어졌다. 이 경계는 아침 보고에 후보로 올라온 중복 일정과 집중 시간을 실제 달력으로 옮길 때 적용된다. 문서를 잘못 고치면 되돌리면 되지만 일정을 잘못 만들면 초대가 나가고 다른 사람의 달력이 바뀐다. 취소해도 상대는 이미 그 시간을 비웠거나 다른 약속을 옮겼을 수 있다. 가드의 핵심은 요청의 단계를 식별하는 키다. 같은 요청이 어느 단계까지 갔는지를 키로 표시하고 완료되었거나 위임되었거나 외부 대기이거나 억제된 상태는 같은 키로 다시 열 수 없다. 사람이 자동으로 만들어진 일정을 지우면 지웠다는 표식이 남고 같은 키는 다시 만들어지지 않으며 표식 없이 이루어진 동작은 거부된다. 삭제 권한은 극도로 좁게 열려 있어서 완료 증거가 있는 확인용 블록과 중복 생성된 항목과 확정된 변경으로 대체된 참석자 없는 개인 일정만 지울 수 있고 실제 회의와 강연과 초대와 반복 일정은 건드리지 않는다. 기록은 미리 남기고 짝으로 닫는데 시작만 남고 종료가 없으면 이후의 모든 일정 변경이 중단되고 자동으로 되돌리거나 다시 시도하지 않는다. 중단된 상태를 사람이 확인하고 푸는 편이 반쯤 바뀐 달력을 기계가 추측으로 정리하는 것보다 싸다. 일정 변경 한 건은 다음 순서로 진행한다.
하지 않을 일의 목록은 할 일의 목록보다 짧아지지 않는다. 오래된 인계를 만났을 때 원본을 뒤져 빠진 것을 메우지 않는 것이 첫 번째다. 하류 작업이 상류의 판정을 다시 해석하지 않는 것이 두 번째이고 하류가 원자료를 다시 뒤지는 것은 명시적으로 금지되어 있다. 보고할 것이 없을 때 정해진 침묵 토큰을 돌려주는 것이 세 번째다. 네 번째는 프롬프트마다 반복해서 적혀 있는 금지 목록이다. 메일을 자동으로 보내지 않고 댓글을 자동으로 달지 않으며 메일과 파일과 문서를 지우지 않고 예약 작업의 정의를 스스로 만들거나 고치거나 멈추거나 지우지 않는다. 이미 처리된 항목을 다시 올리지 않는 것도 명시해야 하는 규칙인데, 답장한 메일과 닫힌 할 일과 지난 회의는 사람이 판단을 끝낸 것이므로 다시 올라오면 같은 판단을 다시 하게 만든다. 처리 여부를 판정하려면 항목마다 식별자가 필요하고 식별자가 없으면 이 규칙은 지킬 수 없다. 미팅 자료가 준비되지 않았는데 미리 폴더를 만들어 두는 일도 하지 않는다. 빈 폴더가 생기면 다음 세션의 작업이 그 미팅이 준비되고 있다고 읽고 사람도 어느 것이 실제로 진행 중인 일인지 헷갈리게 된다. 논문을 수집하는 작업이 새로 찾은 것을 진행 중인 과제 하나에 억지로 연결하는 일도 하지 않는다. 관련이 없는 논문을 관련 있는 것처럼 붙이면 그 과제의 문헌 목록이 조금씩 오염되고 나중에 그 목록을 근거로 판단할 수 없게 된다. 제목과 초록만 보고 검색용 기록을 만들어 두는 것도 금지되어 있다. 사람에 관한 판단을 만들지 않는 것과 불확실한 일정 후보를 확정된 일정으로 만들지 않는 것도 여기에 속한다. 확실하지 않은 것을 확실한 것처럼 표현하면 사람의 판단이 그만큼 잘못된 전제 위에서 이루어진다. 모른다고 보고하는 상태를 설계 안에 넣지 않으면 이 사슬은 아는 척하는 쪽으로 기운다. 하지 않을 일의 목록은 각 작업의 정의 문서 안에 함께 적어 두고 새 실패를 겪을 때마다 한 줄씩 늘린다.
처음부터 여러 개의 예약 작업을 만들면 대부분 실패한다. 무엇을 자동화할지 정하기 전에 무엇을 반복해서 확인하는지부터 적어야 하고 그 목록은 일주일쯤 실제로 관찰해야 나온다. 기억에 의존해 적으면 매일 봐야 한다고 생각하는 것이 적히는데 실제로 매일 보는 것과 그렇게 생각하는 것은 겹치지 않는 경우가 많다. 두 번째 단계가 가장 자주 생략되고 가장 중요하다. 어떤 정보원을 보고 아무 결정도 내리지 않고 있다면 그 정보원은 확인을 그만둘 대상이고 그것을 자동화하면 읽지 않는 보고서가 하나 늘어날 뿐이다. 네 번째와 다섯 번째를 앞쪽에 둔 이유는 두 항목이 나중에 붙이기 가장 어려운 것들이기 때문이다. 인계 형식과 판정 권한을 정하지 않은 채 작업 수를 늘리면 며칠 뒤에 어느 작업이 어느 판정을 내렸는지 되짚을 수 없다. 읽기 전용으로 한 달쯤 돌려 보면 처음 설계한 규칙 중 절반 정도는 고치게 되고 그 고침이 끝나기 전에 발송이나 일정 변경 권한을 붙이면 검증되지 않은 규칙이 되돌릴 수 없는 행동으로 나간다.
권한을 여는 순서에는 기준이 하나 있다. 결과를 사람이 확인하기 전 상태로 되돌릴 수 있는가다. 읽기와 수집과 정리는 처음부터 열어 두었다. 잘못 모았으면 다시 모으면 되고 잘못 정리한 문서는 고치면 된다. 밖으로 나가는 동작과 다른 사람의 일정에 영향을 주는 동작은 열지 않았다. 메일 발송이 여기 해당하고 남의 달력에 무언가를 넣거나 옮기는 일도 한곳에 있으며 두 동작 모두 되돌리는 비용을 상대가 함께 치른다. 사람의 화면을 건드리는 동작도 요청받은 턴에만 하도록 막아 두었는데 예약 작업이 새벽에 돌면서 팝업이나 권한 대화상자를 띄우면 그것을 누르는 사람은 무엇에 승인하는지 모르는 상태로 누르게 된다. 자동화가 열 수 있는 범위는 결정에 필요한 재료를 모아 두는 데까지이고 그 재료로 무엇을 할지는 사람이 그날 아침에 정한다.
첫 번째 문제는 논문 수집 작업에서 생겼다. 자료를 모으는 작업은 원문 확인까지 마쳤지만 다음 작업이 읽을 인계 파일을 만들지 못했다. 뒤이어 실행된 아침 보고 작업은 인계 파일의 날짜를 검사한 뒤 보고서에 오늘 논문 수집 결과를 확인할 수 없음이라고 적고 해당 항목을 비웠다. 이 한 줄을 보고서에서 읽은 뒤에야 앞 작업이 중간에 멈췄다는 사실을 알았다. 전날 파일을 그대로 사용했다면 보고서는 정상적으로 완성됐을 것이고, 사람은 그날 새 논문이 없었다고 오해했을 것이다.
이 문제를 겪은 뒤 인계 파일에는 생성 시각과 완료 상태를 반드시 적게 했다. 다음 작업은 두 값을 확인한 뒤에만 내용을 사용한다. 인계 파일의 날짜가 오늘과 다르거나 완료 표시가 없으면 해당 계열을 미확인으로 보고하고 처리를 멈춘다. 앞 작업의 완료 여부는 다음 작업이 실제로 읽을 수 있는 인계 파일로 판정하며, 실행 기록의 진행 중이나 성공 표시는 참고로만 쓴다.
두 번째 문제는 외부 발송에서 생겼다. 보고서를 만드는 작업이 파일 작성과 발송을 함께 맡고 있었는데, 그 작업의 발송 설정이 비어 있었다. 보고서 파일은 매일 만들어졌고 실행 기록에도 오류가 없었지만 실제 발송은 며칠 동안 이루어지지 않았다. 파일 생성 성공을 발송 성공으로 잘못 계산한 것이다.
그 뒤로 보고서 작성과 발송을 분리했다. 각 작업은 보낼 내용을 대기 폴더에 저장하고, 외부 발송은 전용 작업 하나만 맡는다. 전용 작업은 발송에 성공하면 영수증 파일을 남긴다. 원래 작업은 이 영수증을 확인한 뒤에야 해당 항목을 처리 완료로 기록한다. 보고서 파일이 있다는 사실은 작성이 끝났다는 증거이고, 영수증 파일은 발송이 끝났다는 증거다.
두 실패에서 세 가지 규칙을 정했다.
예약 자동화의 목표는 실패가 정상처럼 보이지 않게 하는 데 있다. 실패 사실이 보고서와 파일에 남아야 사람이 원인을 확인하고 다시 시작할 수 있다.
자동화 코드는 어딘가의 저장소에 있지만 문제가 생겼을 때 코드부터 읽는 것은 가장 느린 경로다. 운영 폴더가 네 층으로 갈려 있는 이유가 여기에 있다. 규칙이 한 층에 있고 가변 상태와 인계가 다른 층에 있고 덧붙이기만 하는 실행 기록이 또 다른 층에 있고 사람이 읽는 산출물이 마지막 층에 있다. 조사를 시작할 때는 규칙 층에서 그 작업이 무엇을 하기로 약속했는지를 읽고 그다음 인계 파일이 만들어진 날짜와 마지막 실행 기록을 함께 본다. 산출물은 오늘 날짜인데 인계는 며칠 전 것인 상태가 실제로 자주 발생하고 산출물만 보면 그 상태가 보이지 않는다. 이 배치가 갈라 놓는 것은 자동화가 무엇을 했는가와 자동화가 무엇을 하기로 약속했는가다. 둘을 구분하지 않으면 자연어 보고서만 읽고 원인을 추정하게 되고 추정은 대체로 틀린다. 약속이 문서에 있으면 어긋난 지점이 코드의 문제인지 계약의 문제인지 판정할 수 있고 계약이 틀렸다면 계약을 고치고 코드가 계약을 지키지 않았다면 코드를 고친다.
규칙은 상황과 행동과 검증의 세 요소로 쓴다. 어떤 상황에서 무엇을 하고 무엇을 확인해야 지켰다고 말할 수 있는지가 한 문장 안에 들어가지 않으면, 다음 세션의 에이전트가 같은 결정을 재현하지 못한다. 무엇을 규칙으로 기록할지도 조건이 정해져 있다. 같은 혼선이 두 번 이상 반복되었거나 한 번이라도 비용이 크거나 되돌리기 어려웠거나 사람이 앞으로 이렇게 하라고 교정했거나 다음 에이전트가 같은 결정을 재현해야 하는 경우다. 한 사례에만 해당하는 파일명과 상태, 검증하지 않은 임시 회피책, 특정 도구를 기본값으로 만드는 취향, 검증할 수 없는 감상형 문장은 기록하지 않는다. 규칙은 처음에 문서로 적는다. 반복해서 확인해야 하는 항목은 검사 코드로 만들고, 승인 없이 실행되면 안 되는 동작은 코드가 실행을 중단하도록 막는다. 일정 변경은 현재 코드로 막혀 있지만 나머지 규칙 대부분은 자연어 지침에 의존한다. 어떤 규칙이 문서에만 적혀 있고 어떤 규칙이 검사나 코드로 강제되는지를 구분해 기록해야 한다.
규칙에 무엇을 적는가도 정해져 있다. 특정 사례와 날짜와 세션은 넣지 않고 일반형 문장만 쓰며 그래야 다음 사람이 자기 상황에 그 문장을 대볼 수 있다. 이미 설치된 도구를 다시 설치하는 일이 스무 번 넘게 반복된 뒤에 도구를 먼저 확인하고 설치를 나중에 하라는 한 줄이 붙었고 그 한 줄이 같은 낭비를 끊었다. 자동화의 계약 문서가 다른 작업 공간의 규칙을 복제하지 않는 것도 같은 위생에 속한다. 복제 대신 절대경로로 가리키고 그 계층에는 심볼릭 링크를 만들지 않으며 모든 문서를 실제 파일로 둔다. 모든 폴더에 규칙 문서를 만들지도 않는다. 판단이 자주 필요하지 않은 업무에는 규칙이 없어도 되고 규칙의 양은 그 업무가 얼마나 자주 판단을 요구하는지를 따라간다. 예약 목록 자체도 관리 대상이다. 어떤 작업이 어떤 입력을 받아 어떤 출력을 내고 지금 켜져 있는지를 적은 목록을 유지하고 실제로 등록된 예약과 주기적으로 대조하는데 문서에는 있는데 실제로는 꺼져 있는 작업과 실제로는 도는데 문서에 없는 작업이 가장 흔한 부패다. 앞의 경우 사람은 오지 않을 보고를 기다리고 뒤의 경우 아무도 읽지 않는 결과가 매일 쌓인다. 성숙도에 가까운 지표는 그 목록과 실제가 일치하는지다. 계약 문서에는 한 달에 한 번 무엇을 검토할지도 정해 둔다. 예약 목록과 실제 활성 작업이 일치하는지, 그리고 지난달에 이 자동화 때문에 실제로 바뀐 판단이 있었는지가 검토 대상이다. 뒤쪽이 계속 비어 있다면 그 자동화는 정보를 만들고 있을 뿐 결정을 돕고 있지 않다는 뜻이고 그때는 그 작업을 끄는 편이 낫다.