meta
조정 파일의 진짜 비용은 디스크가 아니라 에이전트의 'context 오염'이었다
· Ascendy Engineering
TL;DR
- 여러 레포가 각자 거버넌스로 도는 멀티-에이전트 프로젝트에서, 레포끼리 일을 넘기는 조정(coordination) 트래픽 — “이거 너희가 해줘”, 답장, 상태 핑 — 을 markdown 파일로 주고받다가 GitHub Issues로 옮겼다.
- 처음엔 “레포가 커진다”가 이유인 줄 알았다. 그런데 데이터를 보니 디스크는 문제가 아니었다 — 조정 파일 다 합쳐 수백 KB, 코드의 한 자릿수 %.
- 진짜 비용은 따로 있었다. 가장 underrated한 건 — 에이전트가 작업하며 치는 매
grep/ls가 이 파일들을 전부 context window로 끌고 들어와 토큰을 태운다. 사람에겐 ‘파일 좀 많네’지만, 에이전트 워크플로엔 매 턴 무는 세금이다. - 그리고 blanket 교체가 아니라 문서 타입으로 잘랐다 — ephemeral 조정만 Issues로, durable 결정문은 git, 에디토리얼은 파일. 큰 결정은 1초였고, 비용은 디테일에 숨어 적대적 리뷰 4라운드가 나왔다.
이 글에 대하여. 멀티-레포 셋업의 거버넌스 변경 기록이다. 실제 레포·조직 이름과 내부 번호는 일반화했다. 같은 적대적 검증 결의 루프 엔지니어링의 심장은 검증이다, 토큰이 비용의 대리지표라는 토큰을 아끼라는 회사, 펑펑 쓰라는 회사와 이어진다.
파일로 일을 넘기던 방식
우리 셋업은 여러 레포(백엔드·프론트엔드·블로그·인프라)가 각자 자기 거버넌스로 돈다. 레포끼리 일을 넘길 때 — “이거 너희 쪽에서 해줘”, 그에 대한 답장, “어떻게 돼가나” 상태 핑 — 그동안 markdown 파일로 주고받았다. 받는 레포의 intake 디렉터리에 파일을 떨구면, 그 레포 에이전트가 읽고 처리하는 방식이다.
이걸 GitHub Issues로 옮겼다. 단, 통째로 바꾼 게 아니라 문서 타입으로 잘랐다.
| 타입 | 성격 | 목적지 |
|---|---|---|
| 핸드오프·답장·상태 핑 | ephemeral 조정 | → Issues |
| 결정문·설계 기록 | durable (몇 년 뒤 인용·PR 리뷰·벤더 독립) | git 유지 |
| 에디토리얼 intake | 별도 편집 파이프라인 | 파일 유지 |
첫 진단이 틀렸다 — 디스크가 아니었다
첫 프레이밍은 “조정 파일이 쌓여서 레포가 커진다”였다. 그런데 데이터를 보니 디스크는 문제가 아니었다. 조정 파일을 다 합쳐도 수백 KB, docs/는 코드 디렉터리의 한 자릿수 %. 한 해 더 쌓여도 한 자릿수 MB다. 디스크를 이유로 들면 틀린 문제를 푸는 것이었다.
진짜 비용은 셋이었다.
- reply-chain 파일 증식. 대화 하나가 파일 N개가 된다 —
...-reply→...-reply3→...-round5→...-reply2-diagnosis-revisit. 스레드가 아니라 파일이 쌓인다. - 코드 레포 clutter. 대부분 stale인 조정 파일 수십 개가 코드 레포 트리에 섞인다. 진짜 코드와 한 줄 떨어져서.
- 에이전트 context 오염 (제일 underrated). 이게 핵심이다. 에이전트가 작업하며 치는 매
grep, 매ls가 이 파일들을 전부 context window로 끌고 들어와 토큰을 태운다. 사람이 파일 트리를 볼 땐 눈으로 스킵하면 그만이지만, 에이전트에겐 그게 매 턴 무는 세금이다. 조정 파일이 많아질수록, 코드와 무관한 텍스트가 매 작업의 컨텍스트를 야금야금 먹는다.
세 번째가 사람 눈엔 잘 안 보이는 비용이다. 디스크는 GB 단위로 재지만, context window는 토큰 단위로 매 턴 재기 때문이다.
Issues가 셋을 정확히 때린다
GitHub Issues는 이 셋을 하나씩 정확히 해결한다.
- threading — 이슈 하나에 코멘트로 왕복하니, reply-chain 파일이 사라진다.
- git tree 밖 — 이슈는 레포 트리에 없으니 clutter가 0이고, 평소엔
gh로 필요할 때만 조회한다. 즉 에이전트의 평상시 context 오염이 0이다. 파일과 달리, 안 부르면 컨텍스트에 안 들어온다. - native lifecycle — open/close/label/assignee가 파일로는 못 하던 pending → ack → done을 모델링한다. 덤으로
Fixes #NPR 링킹과 크로스-레포 참조.
그럼 왜 전부 옮기지 않았나. durability/portability 트레이드오프 때문이다. 파일-in-git은 영원히 남고, clone에 딸려오고, PR에서 인라인 리뷰되고, 벤더(GitHub)에 독립적이다. Issues는 GitHub DB에 산다. 그러니 몇 년 뒤 인용할 결정문은 git에 두고, 한 번 쓰고 끝인 조정만 Issues로 옮긴다. 그게 선이다.
본편 — 적대적 리뷰 4라운드가 잡은 5함정
거버넌스 변경이라 다른 모델의 적대적 리뷰에 걸었다. “이슈로 옮긴다”는 큰 결정은 1초면 합의된다. 그런데 디테일에서 4라운드가 나왔고, 매 라운드가 실제 함정이었다.
1. 숨은 페이지네이션 cap. “에이전트가 과거 이슈를 다 조회한다”는 게 자동이 아니었다. gh issue list는 기본이 open만 보여주고, 게다가 --limit 30 이다. --state all을 줘도 그 30 cap은 안 풀린다. 컨벤션이 --state all 그리고 명시적 --limit을 둘 다 강제하지 않으면, 과거 조정이 조용히 잘려서 안 보인다.
# 둘 다 줘야 한다 — --state all 하나로는 30개에서 잘린다.
gh issue list --repo <org>/<repo> --state all --label cross-repo --limit 200
2. ack ≠ close. 처음엔 “close = 완료/확인”이라 적었다. 리뷰어가 잡았다 — 확인(봤다, 하는 중)과 완료는 다르다. 확인은 이슈를 open 상태로 둔 채 코멘트로 하고, close는 해결됐을 때만(완료/거절/대체/무대응) 한다. 안 그러면 “받았지만 아직 안 끝남”이라는 pending 상태가 통째로 사라진다.
3. CLI 구문. gh label create cross-repo from-infra ...로 라벨 여러 개를 한 번에 만들려 했는데, 이 명령은 이름 하나만 받는다. loop로 돌려야 한다.
4. 순서 함정. 라벨은 이슈 생성 전에 존재해야 한다(없으면 --label이 실패). “이슈부터 만들고 라벨은 나중에”로 적었던 순서를 뒤집어야 했다.
5. 누락 스윕. 컨벤션을 서술하는 문서가 한 곳이 아니었다. 메인 문서만 고치고 주간-리뷰 루프 문서의 라우팅 서술을 빠뜨린 걸, grep으로 잡았다.
교훈은 하나로 모인다. 큰 결정은 합의가 쉽고, 비용은 디테일에 숨는다. 적대적 리뷰의 값어치는 “이걸 할까”를 정할 때가 아니라, “이렇게 하면 어디서 조용히 깨지나”를 잡을 때 나온다. 위 다섯 개는 전부 “조용히 깨지는” 종류 — 안 잡았으면 한참 뒤에 “왜 과거 이슈가 안 보이지?”로 돌아왔을 것들이다.
메타 아이러니
마지막으로 하나. 이 변경을 제안한 결정-seed 문서 자체가, markdown 파일로 쓴 크로스-레포 핸드오프였다. 즉 자기가 폐지하려는 바로 그 메커니즘을 타고 전달됐다. 맞는 선택이었다 — 새 컨벤션이 아직 없었으니 당시엔 그게 규칙이었다. 그리고 적절하게도, 아마 마지막 파일 기반 핸드오프 중 하나였다.
가져갈 것
- 멀티-에이전트 워크플로에선 “파일이 많다”가 디스크 문제가 아니다. 진짜 비용은 에이전트가 매 grep/ls마다 무는 context 세금이다. 사람 기준으로 비용을 재면 틀린 문제를 푼다.
- 조정(ephemeral)과 기록(durable)을 같은 메커니즘에 두지 마라. 한 번 쓰고 끝인 건 on-demand로 부를 수 있는 곳(Issues)에, 몇 년 뒤 인용할 건 git에. 자르는 축은 디스크가 아니라 durability/portability다.
- 큰 결정은 합의가 쉽고, 비용은 디테일에 숨는다. 적대적 리뷰는 결정이 아니라 디테일에서 값을 한다.
저작·인용: 이 글은 Ascendy Engineering이 작성했으며 출처 표기 시 재인용 가능합니다. 잘못된 정보를 발견하면 GitHub 이슈로 알려주세요.
Tags: multi-agent, developer-workflow, github-issues, context-window, governance, war-story, adversarial-review