에이전트 여럿에게 일을 시킬 때, 누가 무엇을 언제 집는가.

안 A   에이전트가 큐를 보고 스스로 다음 일감을 집는다
안 B   사람이 이슈를 낱개로 배정하고, 에이전트는 배정받은 것만 한다

안 A는 사람이 병목이 아니다. 밤에도 돌고 주말에도 돈다. 대신 사람이 인지하지 못하는 사이에 프로젝트가 굴러간다.

이 글은 안 B를 고른 기록이다. 그리고 그 선택이 나머지 규칙을 거의 다 결정했다 — 라벨을 몇 개 둘지, 락을 언제 걸지, 죽은 에이전트를 어떻게 감지할지가 전부 여기서 따라 나왔다.


먼저: 에이전트는 사람이 있을 때만 돈다

이건 원칙이 아니라 전제다. 나머지를 읽기 전에 세워야 한다.

무인 운영을 하지 않기로 하면 설계해야 할 것이 통째로 사라진다.

밤·주말에 락이 걸린 채 방치되면?        → 그 시간에 아무것도 안 돈다
에이전트가 죽은 걸 어떻게 감지하나?      → 죽으면 사람이 본다
일감이 생긴 걸 어떻게 알아채나?          → 사람이 배정할 때 안다

폴링도 웹훅도 하트비트도 필요 없다. 없앤 게 아니라 애초에 안 생겼다.

대가는 분명하다. 야간과 주말에는 처리량을 벌 수 없다. 그리고 그 대가를 감수한 이유가 도입부의 안 A를 버린 이유와 같다 — 사람이 인지 못 한 채 굴러간 프로젝트를 되돌리는 비용이, 늦어지는 비용보다 크다.


원칙 1. 계층 순서가 곧 의존이라 트래커에 의존을 적지 않는다

기능 하나를 부모 이슈 하나와 계층 이슈로 나눈다.

기획 → 프론트 → 백엔드 → 테스트

정해야 했던 건 이거였다 — 이슈 사이의 선후를 어디에 적는가.

안 A — 트래커의 의존 기능을 쓴다. Jira는 blocks/is blocked by 링크가 있고 GitHub은 본문에 텍스트로 적는다. 얻는 것은 관계가 임의로 정해질 수 있다는 것이다. 어떤 기능은 백엔드가 먼저일 수도 있으니까.

버린 이유는 트래커 둘의 표현이 다른 것이다. Jira 링크와 GitHub 텍스트를 양쪽 다 지원하면 어댑터가 둘로 갈리고, 갈리는 순간 한쪽만 고쳐져 어긋난다. 이 저장소는 그 실수를 이미 겪었다 — .claude/skills.agents/skills가 4개 어긋난 채 배포됐다.

골랐다 — 안 B, 계층 순서를 표준에 고정한다. 기획이 프론트보다 앞이라는 것은 어디에도 안 적혀 있어도 참이다. 적을 곳이 없으니 어긋날 곳도 없다.

대신 순서가 다른 기능이 나오면 예외를 만들어야 한다. 지금은 그런 기능이 없어서 미룬다 — 없는 예외를 미리 설계하면 쓰이지 않는 구조가 남는다.


원칙 2. 이슈를 미리 전부 만든다 — 진행 순서가 아니라 수신함이기 때문이다

계층 이슈 넷을 언제 만드는가.

안 A — 착수 가능한 것만 만든다. 기획 이슈를 먼저 만들고, 끝나면 프론트 이슈를 만든다. 얻는 것은 트래커가 깨끗하다는 것이다. 열린 이슈가 곧 할 일이라 목록이 정직하다.

버린 이유는 이슈가 수신함 역할을 못 하는 것이다. 프론트가 작업하다 백엔드가 알아야 할 사실을 발견했을 때, 백엔드 이슈가 아직 없으면 적을 자리가 없다. 그러면 그 정보는 사람 머릿속이나 세션 안에만 남고, 세션이 끊기면 사라진다.

골랐다 — 안 B, 처음부터 전부 만든다. 안 A가 못 지켜주는 것은 아직 시작 안 한 계층에 정보를 남길 자리다. 이슈는 할 일 목록이기 전에 다른 계층이 글을 남기는 우편함이다.

대가는 열린 이슈가 실제 할 일보다 많아 보이는 것이다. 그건 blocked 라벨이 가린다(원칙 4).

해당 계층이 없는 기능이면 그 이슈는 만들지 않는다. 그리고 아래 규칙들은 전부 「존재하는 형제만」 본다 — 이게 있어야 계층이 빠진 기능에서도 규칙이 성립한다.


원칙 3. 프론트는 요구를 내고 백엔드가 설계한다

API를 누가 정하는가. 여기가 계층 사이에서 제일 자주 부딪히는 자리다.

계층 정하는 것
기획 왜 만드는가, 사용자가 무엇을 필요로 하는가, 어떻게 해야 편리한가
프론트 무엇이 필요한지 — 화면이 요구하는 데이터와 API 목록
백엔드 API 설계. 화면이 없는 것은 스펙까지 전부

안 A — 기획이 API까지 설계한다. 얻는 것은 가장 앞 계층에서 다 정해져 뒤가 단순해지는 것이다. 뒤 계층은 받아서 만들기만 하면 된다.

버린 이유는 기획이 답할 수 없는 질문을 떠안는 것이다. 페이지 크기 상한이나 정렬 키 허용 목록은 인덱스 비용에서 나오는데, 기획은 그 비용을 볼 자리가 없다. 그리고 기획의 몫은 왜와 무엇까지고 설계는 그 밖이다.

골랐다 — 안 B, 프론트는 요구를 내고 백엔드가 설계한다. 안 A가 못 지켜주는 것은 검증자의 존재다. 요구를 내는 쪽과 설계하는 쪽이 갈려 있으면 백엔드가 “그건 이렇게 주는 게 맞다” 또는 “그건 못 준다”고 말할 자리가 생긴다. 안 A에서는 기획이 낸 설계를 되돌릴 자리가 없다.

대가는 왕복이 한 번 는다는 것이다. 프론트가 낸 요구를 백엔드가 구현할 수 없을 때, 스스로 바꾸지 않고 사람에게 올린다(원칙 8).

각 계층은 자기 결과를 자기 이슈 본문에 적는다. 부모 이슈나 별도 문서에 모으는 안도 있었는데, 뒤 계층이 읽을 곳이 이슈로 통일되는 편이 낫다 — 원칙 2가 이슈를 수신함으로 정했으므로 쓰는 곳과 읽는 곳이 같아야 한다.


원칙 4. blocked확인 필요를 가른다 — 기다리는 대상이 다르다

이슈가 못 나아가는 이유는 둘인데 성격이 다르다.

라벨 무엇을 기다리나 누가 떼나
blocked 선행 계층 앞 계층을 끝낸 에이전트
확인 필요 사람의 판단 사람

안 A — 라벨 하나로 합친다. “막힘”은 어차피 막힘이니 하나면 된다. 얻는 것은 라벨이 적어 규칙이 짧아지는 것이다.

버린 이유는 푸는 규칙이 하나로 안 끝나는 것이다. blocked의 해제 규칙은 “앞 계층을 끝낸 에이전트가 푼다”인데, 이걸 합친 라벨에 그대로 적용하면 에이전트가 사람 판단 대기까지 풀어버린다. 사람이 아직 안 봤는데 다음 작업이 시작된다.

골랐다 — 안 B, 둘로 가른다. 라벨이 하나 늘지만 푸는 주체가 라벨마다 하나로 정해진다. 이름이 다르면 규칙도 안 섞인다.

확인 필요가 붙은 동안 touch 락은 유지한다(원칙 6). 푸는 안도 검토했는데, 반쯤 고친 작업물 위에 다른 에이전트가 덮어쓰면 사람이 상태를 알 수 없다. 무인 운영이 없어 장시간 잠기는 상황도 거의 안 생긴다.


원칙 5. 자기 blocked는 자기가 떼지 않는다

라벨을 가르고 나면 누가 떼느냐가 남는다.

안 A — 착수하려는 에이전트가 스스로 확인하고 뗀다. 앞 계층이 다 닫혔는지 보고 괜찮으면 뗀다. 얻는 것은 떼는 시점이 착수 시점과 같아 지연이 없다는 것이다.

버린 이유는 검문이 무의미해지는 것이다. 자기가 자기 통행증을 발급하면, 판정을 잘못했을 때 막아줄 것이 없다.

골랐다 — 안 B, 앞 계층을 끝낸 에이전트가 뗀다. 쓰는 권한과 푸는 권한이 갈려야 검문이 산다. 사람이 실수로 배정해도 라벨이 붙어 있으면 착수 절차 2번에서 멈춘다.

해제 규칙은 이렇다 — 자기 일을 끝내고 다음 계층 이슈를 보고, 그것보다 앞 계층인 형제가 전부 닫혔으면 뗀다. 하나라도 열려 있으면 떼지 않는다.

이 규칙이 합류점을 저절로 처리한다. 테스트는 프론트와 백엔드를 둘 다 기다리는데, 백엔드가 끝나고 봤을 때 프론트가 아직 열려 있으면 안 뗀다. 분기와 합류를 위한 별도 규칙이 필요 없다.

그리고 읽기 규칙이 같은 집합을 쓴다. 착수할 때 “자기보다 앞 계층인 형제를 전부 읽는다”인데, 해제 규칙의 “앞 계층인 형제가 전부 닫혔는가”와 대상이 똑같다. 개념 하나가 둘을 덮는다.


원칙 6. touch 겹침은 착수 전에만 막고 작업 중에는 막지 않는다

이슈에는 만질 파일 목록(touch)이 붙는다. 겹치면 어떻게 하는가.

여기가 이 표준에서 유일하게 대칭이 아닌 규칙이고, 그게 의도다.

시점 겹치면
착수 전 동시에 진행하지 않는다 사람이 배정 순서만 바꾸면 되니 회피가 공짜
작업 중 발견 그냥 진행하고 머지 충돌로 푼다 이미 투자했으니 중단이 비싸다

안 A — 양쪽 다 막는다. 작업 중에 touch 밖 파일을 만져야 하면 멈추고 다시 확인한다. 얻는 것은 충돌이 아예 안 나는 것이다.

버린 이유는 중단 비용이 이미 치러진 뒤라는 것이다. 절반쯤 고친 상태에서 멈추면 그 작업물을 어떻게 둘지가 새 문제가 된다.

안 B — 양쪽 다 안 막는다. 전부 낙관적으로 가고 충돌은 머지에서 푼다. 버린 이유는 착수 전 회피가 공짜인데 안 쓰는 것이다. 아직 시작 안 했으면 사람이 배정 순서만 바꾸면 된다.

골랐다 — 안 C, 비용이 다르니 규칙도 다르게 둔다. 같은 겹침이라도 언제 발견했느냐에 따라 회피 비용이 다르다.

그리고 파일 단위 락은 실제 충돌보다 과하게 잡는다. 같은 파일이라도 다른 곳을 고치면 git이 알아서 합친다. 그래서 작업 중에는 붙여보고 정말 나는 것만 푼다.

실효 범위는 같은 계층 안이다. 프론트와 백엔드는 프로젝트 파일이 애초에 안 겹친다. 그래서 계층 간 안전은 락이 아니라 원칙 3의 소유권 규칙 하나에 전적으로 걸려 있다.


원칙 7. 에이전트마다 대기 브랜치를 둔다 — 이점이 아니라 git 제약 때문이다

이 항목은 한 번 정했다가 뒤집었다. 원래는 “워커별 브랜치를 만들지 않는다”였다.

원래의 버린 이유는 지금도 유효하다 — 담을 내용이 없고 동기화 지점만 는다. 대기 브랜치는 아무것도 안 담는데 브랜치를 하나 더 관리해야 한다.

뒤집은 것은 이점을 발견해서가 아니라 다른 길이 막혀서다. 에이전트마다 워크스페이스가 따로인데 별도 클론이 아니라 워크트리다. 워크트리는 .git 하나를 공유하고, 한 브랜치는 한 워크트리에서만 체크아웃된다.

워커 A 가 dev 에 서 있다
  → 워커 B 는 dev 로 이동조차 못 한다

별도 클론이었으면 안 걸렸을 문제다. 그래서 이건 안이 틀렸던 게 아니라 전제가 바뀐 것이다. 클론을 파주는 도구가 생기면 되돌릴 후보로 남는다.

맞추는 방법에도 안이 둘이었다.

안 A — 대기 브랜치에 dev를 머지한다. 익숙한 방법이다. 버린 이유는 대기 브랜치에만 쌓이는 머지 커밋이 어디로도 안 가는 것이다. 대기 브랜치는 푸시하지 않으므로 그 커밋은 영원히 로컬에 남는다.

골랐다 — 안 B, git reset --hard origin/dev로 덮어쓴다. 담는 것이 없으니 히스토리를 보존할 이유도 없다.

git fetch origin
git switch <대기 브랜치>
git reset --hard origin/dev

이 한 줄이 커밋 대기 표준과 부딪혔고, 그쪽을 고쳐서 풀었다. 그 표준은 되돌리는 명령을 금지하되 「담는 것이 없다고 표준이 선언한 브랜치를 통합 브랜치 최신으로 맞출 때 + 작업 트리가 깨끗할 때」를 예외로 둔다. 대기 브랜치가 그 선언이고, 깨끗함은 착수 1번이 검사한다.

작업 브랜치는 대기 브랜치에서 딴다. 방금 덮어썼으므로 내용은 dev와 같고, PR 대상만 dev로 되돌린다. 결과가 모이는 곳이 기준이다.


원칙 8. 막히면 이슈를 닫지 말고 확인 필요로 올린다

에이전트가 못 하겠다고 판단했을 때 무엇을 하는가.

안 A — 자기가 판단해서 앞 계층의 결정을 바꾼다. 프론트가 낸 요구를 백엔드가 구현할 수 없으면 스펙을 조정해서 진행한다. 얻는 것은 사람을 안 부르고 계속 도는 것이다.

버린 이유는 끊을 주체가 없는 루프가 생기는 것이다. 백엔드가 스펙을 바꾸면 프론트가 다시 맞춰야 하고, 프론트가 맞추다 또 못 맞추면 다시 백엔드로 간다. 그리고 이건 기획 의도까지 되돌아가야 풀리는 문제일 수 있다 — 그 판단은 에이전트의 소유 범위 밖이다.

골랐다 — 안 B, 원인을 이슈 본문에 적고 확인 필요를 붙이고 멈춘다. 닫지 않는다.

닫지 않는 이유가 따로 있다. 닫힌 이슈는 원칙 5의 해제 규칙에서 「끝났다」로 읽힌다. 막혀서 닫으면 다음 계층의 blocked가 풀리고, 안 끝난 것 위에서 다음 작업이 시작된다.

PM도 예외가 아니다. PM은 판단하지 않는다 — 자기 일을 하다 확인 필요를 발견하면 그 자리에서 사람에게 올린다.


원칙 9. 사람 개입 네 지점을 줄이지 않는다

  언제 빈도
1 이슈 배정 (낱개로) 티켓마다
2 커밋 전 확인 티켓마다
3 확인 필요 처리 예외
4 충돌 해결 배정 예외

앞의 둘이 티켓마다라 이게 그대로 처리량 상한이 된다. 줄이자는 안이 당연히 나왔다.

안 A — 배정을 자동화한다. 에이전트가 큐에서 집는다. 도입부의 안 A와 같다. 버린 이유도 같다 — 사람이 인지 못 한 채 굴러간다.

안 B — PR에서 한 번 더 본다. 반대 방향이다. 안전이 는다. 버린 이유는 커밋 전에 이미 본 것을 두 번 보는 것이다. 같은 작업을 두 번 보는 왕복이 그대로 비용이다. 그래서 PR은 자동 머지고, CI가 유일한 자동 검문이 된다.

골랐다 — 넷 다 유지하고 대신 에이전트를 2~3개로 낮게 간다. 개입을 줄이는 대신 개입이 감당되는 규모로 맞춘다.

여기서 2번(커밋 전 확인)을 없애는 안은 아예 안 올렸다. 커밋 대기 표준이 이미 답한 자리라서다 — 커밋해버리면 멈출 계기가 사라진다.

그래서 “에이전트가 부족한가”의 판정 기준이 뒤집힌다.

에이전트가 놀고 사람이 여유    → 늘린다
둘 다 바쁘다                  → 상한이다
배정 대기가 쌓인다            → 에이전트 부족이 아니라 사람 부족

마지막 줄이 핵심이다. 대기가 쌓일 때 에이전트를 늘리면 더 나빠진다.


원칙 10. 테스트만 새 세션으로 연다

기획·프론트·백엔드는 세션을 이어간다. 기본 컨텍스트(CLAUDE.md와 코딩 스탠다드)를 매번 다시 읽지 않기 위해서다.

테스트만 새 세션이다. 같은 세션이 구현을 들고 있으면 자기가 만든 것에 맞춘 테스트가 나온다. 구현이 틀렸을 때 테스트도 같이 틀린다.

이 규칙이 원칙 5의 읽기 규칙과 정면으로 부딪힌다. 읽기 규칙상 테스트는 앞 계층 형제를 전부 읽는데, 백엔드 이슈를 읽으면 구현 의도가 그대로 들어와 세션을 가른 의미가 사라진다.

푸는 방향은 보인다 — 읽을 것을 계약(요구·API 스펙)과 구현으로 갈라야 한다. 그러려면 백엔드 이슈 본문에 둘이 나뉘어 적혀야 한다.

아직 안 정했다. 테스트 에이전트 상세를 따로 논의하기로 미뤘고, 그 안에 이 문제가 있다. 장부에 열어뒀다.


판단 기준 정리

질문
에이전트가 일감을 집나 사람이 배정한다 인지 못 한 채 굴러가면 되돌리는 값이 더 크다
의존을 어디에 적나 안 적는다 계층 순서가 곧 의존이라 어긋날 곳이 없다
이슈를 언제 만드나 처음에 전부 진행 순서가 아니라 수신함이라서
API를 누가 설계하나 백엔드 요구와 설계가 갈려야 검증자가 생긴다
라벨을 몇 개 두나 푸는 주체가 달라서
자기 blocked를 떼나 못 뗀다 쓰는 권한과 푸는 권한이 갈려야 검문이 산다
touch가 겹치면 착수 전만 막는다 회피 비용이 시점마다 다르다
대기 브랜치를 왜 두나 워크트리가 .git을 공유해서 이점이 아니라 제약이다
막히면 닫나 안 닫는다 닫으면 다음 blocked가 풀린다
개입을 줄이나 안 줄이고 에이전트를 줄인다 대기가 쌓이면 사람 부족이다

이 표준을 정하기까지

시작은 티켓에 좌표를 담는 얘기였다. PM 에이전트가 코드를 읽어 티켓을 만드는데, 작업 에이전트가 그 티켓을 받고 또 탐색한다. 낭비는 “두 번 읽기”가 아니라 “두 번 탐색”이었고, PM이 요약 대신 좌표를 넘기면 없어진다. touch 필드가 거기서 나왔다.

그 좌표를 쓰려면 누가 언제 일을 집는지가 먼저 정해져야 했다. 그래서 운영 쪽으로 넘어왔다.

전제부터 세웠다. “무인 운영을 할 것인가”를 먼저 물었고 안 하기로 하자 설계 항목이 통째로 사라졌다 — 밤·주말 락, 감지 지연, 죽은 에이전트 감지. 이걸 먼저 정하지 않았으면 폴링과 하트비트를 설계하다 시간을 다 썼을 것이다.

라벨을 가르는 자리에서 규칙이 하나로 모였다. blocked 해제 규칙(“앞 계층 형제가 전부 닫혔으면 푼다”)을 세우고 나서 읽기 규칙을 정하려는데, 대상 집합이 이미 같았다. 그리고 그 규칙이 합류점(테스트가 둘을 기다림)과 계층 누락까지 저절로 처리했다. 분기·합류를 위한 별도 규칙을 안 만들어도 됐다.

touch 락에서 대칭을 깼다. 처음엔 착수 전과 작업 중을 같게 두려 했는데, 회피 비용이 시점마다 다르다는 걸 보고 갈랐다. 같은 겹침인데 착수 전에는 배정 순서만 바꾸면 되고 작업 중에는 이미 투자한 것을 버려야 한다.

브랜치에서 판단을 한 번 뒤집었다. “워커별 브랜치를 만들지 않는다”고 정해뒀는데 뒤집혔다. 뒤집은 이유가 취향이 아니라 git 규칙이었다 — 워크트리는 .git 하나를 공유해서 한 브랜치를 둘이 체크아웃할 수 없다. 원래 버린 이유(담을 게 없다·동기화 지점 증가)는 지금도 맞다. 이점이 생겨서 채택한 게 아니라 다른 길이 막혀서 채택한 것이라, 클론을 파주는 도구가 생기면 되돌린다.

그리고 그 브랜치가 커밋 대기 표준과 부딪혔다. 세 군데였는데 — 따는 자리, PR 대상, reset --hard셋 다 그쪽이 명령을 규칙으로 적어둔 자리였다. 이 표준은 그 의도를 하나도 안 어겼고 자구만 어겼다. 그래서 예외를 달지 않고 그쪽을 의도 높이로 다시 썼다. 자세한 경로는 그 글의 원칙 7-1과 9에 있다.

마지막으로 사람 개입을 줄이자는 얘기가 나왔다가 접혔다. 넷 중 앞의 둘이 티켓마다라 그대로 상한이 되는데, 줄이는 대신 에이전트 수를 낮추기로 했다. 그러면서 판정 기준이 뒤집혔다 — 배정 대기가 쌓이는 것은 에이전트 부족의 신호가 아니라 사람 부족의 신호라서, 그때 에이전트를 늘리면 더 나빠진다.

남은 것이 테스트 에이전트다. 세션을 가르기로 했는데 읽기 규칙이 그걸 무너뜨린다는 걸 마지막에 발견했다. 정하지 않고 장부에 열어뒀다 — 읽을 것을 계약과 구현으로 가르려면 백엔드 이슈 본문의 구조부터 정해야 하고, 그건 이 표준의 범위를 넘는다.


정리

  • 에이전트는 사람이 있을 때만 돈다. 이 전제가 무인 운영 장치를 통째로 지운다
  • 계층 순서가 곧 의존이다. 트래커에 안 적으니 어긋날 곳이 없다
  • 이슈는 할 일 목록이기 전에 수신함이다. 그래서 처음에 전부 만든다
  • 프론트는 요구를 내고 백엔드가 설계한다. 갈라야 검증자가 생긴다
  • blocked확인 필요는 푸는 주체가 다르다. 그래서 라벨도 다르다
  • 자기 blocked는 자기가 못 뗀다. 쓰는 권한과 푸는 권한이 갈려야 검문이 산다
  • touch 겹침은 착수 전에만 막는다. 회피 비용이 시점마다 다르다
  • 대기 브랜치는 이점이 아니라 제약 때문에 있다. 워크트리가 .git을 공유한다
  • 막히면 닫지 않는다. 닫으면 다음 계층의 blocked가 풀린다
  • 개입을 줄이는 대신 에이전트를 줄인다. 대기가 쌓이면 사람 부족이다

AI 코드 어시스턴트에 바로 적용하기

Claude Code — .claude/skills/agent-operations-standard/SKILL.md

---
name: agent-operations-standard
description: 여러 에이전트가 이슈로 일감을 주고받으며 동시에 일할 때의 절차. 계층 파이프라인, blocked 라벨, touch 겹침, 브랜치와 머지, 막혔을 때의 처리를 정한다. 이슈를 받아 착수할 때, 작업을 끝냈을 때, 못 하겠을 때 적용한다.
---

# 에이전트 운영 표준

에이전트는 사람이 있을 때만 돈다. 스스로 일감을 집지 않는다.

## 계층 파이프라인

기능 하나를 부모 이슈 하나와 계층 이슈로 나눈다.

```
기획 → 프론트 → 백엔드 → 테스트
```

- **이 순서가 곧 의존이다. 의존 관계를 이슈에 따로 적지 않는다.**
- 계층 이슈는 **착수 가능 여부와 무관하게 처음부터 전부 만든다.** 다른 계층이 정보를 남길 자리가 미리 있어야 한다.
- 해당 계층이 없는 기능이면 그 이슈를 만들지 않는다. 아래 규칙은 **존재하는 형제만** 본다.

## 계층별 소유 범위

| 계층 | 정하는 것 |
|---|---|
| 기획 | 왜 만드는가, 사용자가 무엇을 필요로 하는가, 어떻게 해야 편리한가. **API 설계는 하지 않는다** |
| 프론트 | **무엇이 필요한지** — 화면이 요구하는 데이터와 필요한 API 목록. 그리고 디자인·퍼블·개발 |
| 백엔드 | **API 설계.** 화면이 없는 것(배치·외부 연동·내부 호출·관리자)은 스펙까지 전부 백엔드가 정한다 |
| 테스트 | 계약을 검증한다 |

- 각 계층은 **자기 결과를 자기 이슈 본문에 적는다.**
- **다른 계층의 소유 범위를 침범하지 않는다.** 프론트가 낸 요구를 구현할 수 없으면 스스로 바꾸지 말고 아래 「막혔을 때」로 간다.

## 대기 브랜치

에이전트마다 자기 이름의 대기 브랜치를 하나씩 갖는다. 일감이 없을 때 여기에 서 있고, 작업 브랜치도 여기서 판다.

- **통합 브랜치를 체크아웃하지 않는다.** 에이전트마다 워크트리가 따로여도 `.git`은 하나라, 한 워크트리가 `dev`를 잡으면 나머지는 `dev`로 이동하지 못한다.
- 대기 브랜치는 **로컬 전용이고 담는 것이 없다.** 푸시하지 않고 여기에 직접 커밋하지 않는다. 그래서 착수할 때마다 `dev`와 같게 맞춘다.
- 맞추는 방법은 **`dev`로 덮어쓰는 것**이다. 머지하지 않는다 — 대기 브랜치에만 쌓이는 머지 커밋은 어디로도 가지 않는다.
- **덮어쓰기가 허용되는 근거가 위 두 줄이다.** 커밋 대기 표준은 되돌리는 명령을 금지하되 「담는 것이 없다고 표준이 선언한 브랜치를 통합 브랜치 최신으로 맞출 때 + 작업 트리가 깨끗할 때」를 예외로 둔다. 대기 브랜치가 그 선언이고, 깨끗함은 착수 1번이 검사한다. **둘 중 하나라도 어긋나면 예외가 아니므로 확인받는다.**

```bash
git fetch origin
git switch <대기 브랜치>
git reset --hard origin/dev
```

## 착수

1. **자기 대기 브랜치를 `dev`와 같게 맞춘다.** 커밋하지 않은 변경이 남아 있으면 덮어쓰지 말고 멈춰 사람에게 알린다.
2. **이슈의 `blocked` 라벨을 확인한다. 붙어 있으면 시작하지 않고 사람에게 알린다.**
3. **자기보다 앞 계층인 형제 이슈를 전부 읽는다.**
4. 이슈의 `touch` 목록이 진행 중인 다른 이슈와 겹치면 시작하지 않고 사람에게 알린다.
5. **자기 대기 브랜치에서 작업 브랜치를 판다.** `dev`에서 따지 않는다 — 방금 맞췄으므로 딴 자리의 내용은 `dev`와 같다.

## 작업 중

- **`touch`에 없는 파일을 만져야 하면 그대로 진행한다.** 겹침을 다시 확인하지 않는다. 실제 충돌은 머지에서 푼다.
- 발견한 사실 중 **다른 계층이 알아야 할 것은 그 계층의 이슈에 적는다.**

## 완료

1. **커밋하지 않고 멈춘다.** 변경 요약을 내놓고 기다린다.
2. 커밋하라는 말을 들으면 커밋하고 작업 브랜치를 푸시하고 PR을 연다. **PR 대상은 `dev`다** — 딴 자리는 대기 브랜치지만 그 내용이 출발한 통합 브랜치는 `dev`다.
3. **PR 본문에 이슈 주소를, 이슈 코멘트에 PR 주소를 양쪽 다 적는다.**
4. **PR 머지를 기다리지 않는다.** 자기 대기 브랜치로 돌아와 다음 일감을 준비한다.
5. 자기 이슈를 닫는다.
6. **다음 계층 이슈를 보고, 그것보다 앞 계층인 형제가 전부 닫혔으면 `blocked`를 뗀다.** 하나라도 열려 있으면 떼지 않는다.

## 라벨

| 라벨 | 무엇을 기다리나 | 누가 떼나 |
|---|---|---|
| `blocked` | 선행 계층 | **앞 계층을 끝낸 에이전트** |
| `확인 필요` | 사람의 판단 | 사람 |

- **자기 이슈의 `blocked`는 자기가 떼지 않는다.**
- `확인 필요`가 붙은 이슈의 `touch`는 풀지 않는다.

## 막혔을 때

- **원인을 이슈 본문에 적고 `확인 필요` 라벨을 붙이고 멈춘다. 이슈를 닫지 않는다.**
- 앞 계층이 낸 요구를 구현할 수 없을 때도 같은 절차를 쓴다.
- **스스로 판단해서 앞 계층의 결정을 바꾸지 않는다.**

## 머지 충돌

- 자동 머지가 충돌로 멈추면 **원래 작업한 에이전트가 아니어도 푼다.** 배정받은 에이전트가 양쪽 이슈를 읽고 해결한다.
- 판단이 안 서면 `확인 필요`로 올린다.

## 하지 않는 것

- **스스로 일감을 집지 않는다.** 배정은 사람이 한다.
- **통합 브랜치에 로컬에서 머지하지 않는다.** PR로만 올린다.
- **`dev`를 체크아웃하지 않는다.** 다른 워크트리가 `dev`로 이동하지 못하게 만든다.
- **대기 브랜치에 직접 커밋하지 않는다.** 고칠 파일을 열기 전에 작업 브랜치를 판다.
- **대기 브랜치로 PR을 열지 않는다.** 대상은 `dev`다.
- 자기 이슈의 `blocked`를 떼지 않는다.
- PR을 스스로 머지하지 않는다.

## 이 표준이 정하지 않는 것

프로젝트마다 다르므로 프로젝트 설정에 둔다. 이 파일을 고쳐서 맞추지 않는다.

- 트래커 종류와 접속 방법
- 에이전트 종류별 개수
- 라벨의 실제 이름
- 워크스페이스 경로와 통합 브랜치 이름
- 에이전트별 대기 브랜치 이름(`pm/1` · `plan/1` 같은 것)

GitHub Copilot — .github/instructions/agent-operations-standard.instructions.md

---
description: 여러 에이전트가 이슈로 일감을 주고받으며 동시에 일할 때의 절차
applyTo: "**"
---

# 에이전트 운영

- 에이전트는 사람이 있을 때만 돈다. **스스로 일감을 집지 않는다.** 배정은 사람이 한다.
- 기능 하나를 부모 이슈와 계층 이슈로 나눈다. 계층 순서는 `기획 → 프론트 → 백엔드 → 테스트`다.
- **이 순서가 곧 의존이다. 의존 관계를 이슈에 따로 적지 않는다.**
- 계층 이슈는 착수 가능 여부와 무관하게 **처음부터 전부 만든다.** 다른 계층이 정보를 남길 자리가 필요하다.
- 기획은 왜 만드는지와 무엇이 필요한지까지만 정한다. **API 설계는 하지 않는다.**
- 프론트는 **무엇이 필요한지**(화면이 요구하는 데이터와 API 목록)를 정하고 디자인·퍼블·개발을 한다.
- 백엔드는 **API를 설계한다.** 화면이 없는 것(배치·외부 연동·내부 호출·관리자)은 스펙까지 백엔드가 정한다.
- 각 계층은 자기 결과를 **자기 이슈 본문에** 적는다.
- 에이전트마다 **자기 이름의 대기 브랜치**를 하나 갖고 거기서 기다린다. **`dev`를 체크아웃하지 않는다**`.git`이 하나라 한 워크트리가 `dev`를 잡으면 나머지가 이동하지 못한다.
- 착수할 때 `git fetch origin` 후 대기 브랜치를 **`origin/dev`로 덮어쓴다.** 머지하지 않는다. 커밋하지 않은 변경이 남아 있으면 덮어쓰지 말고 멈춰 사람에게 알린다.
- **작업 브랜치는 자기 대기 브랜치에서 판다.** `dev`에서 따지 않는다.
- 착수 전에 `blocked` 라벨을 확인한다. **붙어 있으면 시작하지 않고 사람에게 알린다.**
- 착수할 때 **자기보다 앞 계층인 형제 이슈를 전부 읽는다.**
- 이슈의 `touch` 목록이 진행 중인 다른 이슈와 겹치면 시작하지 않고 사람에게 알린다.
- **작업 중에 `touch`에 없는 파일을 만져야 하면 그대로 진행한다.** 실제 충돌은 머지에서 푼다.
- 작업을 끝내면 **커밋하지 않고 멈춘다.** 커밋하라는 말을 들으면 커밋·푸시하고 PR을 연다. **PR 대상은 `dev`다.** 대기 브랜치로 열지 않는다.
- PR 본문에 이슈 주소를, 이슈 코멘트에 PR 주소를 **양쪽 다 적는다.**
- **PR 머지를 기다리지 않는다.** 자기 대기 브랜치로 돌아와 다음 일감을 준비한다.
- 자기 이슈를 닫은 뒤, 다음 계층 이슈보다 **앞 계층인 형제가 전부 닫혔으면 `blocked`를 뗀다.** 하나라도 열려 있으면 떼지 않는다.
- **자기 이슈의 `blocked`는 자기가 떼지 않는다.**
- 못 하겠으면 **원인을 이슈에 적고 `확인 필요`를 붙이고 멈춘다.** 이슈를 닫지 않는다.
- 앞 계층이 낸 요구를 구현할 수 없어도 같다. **스스로 앞 계층의 결정을 바꾸지 않는다.**
- `확인 필요`가 붙은 이슈의 `touch`는 풀지 않는다.
- 머지 충돌은 원래 작업한 에이전트가 아니어도 푼다. 배정받으면 양쪽 이슈를 읽고 해결한다.
- 통합 브랜치에 **로컬에서 머지하지 않는다.** PR로만 올린다.
- **대기 브랜치에 직접 커밋하거나 푸시하지 않는다.** 로컬 전용이고 담는 것이 없다.
- PR을 스스로 머지하지 않는다.
- 트래커 종류, 에이전트 개수, 라벨 이름, 워크스페이스 경로, 대기 브랜치 이름은 **프로젝트 설정이다.** 이 파일을 고쳐서 맞추지 않는다.

자신만의 철학을 만들어가는 중입니다.
최상단으로 이동했습니다!
확대 이미지

댓글남기기