


AI 코딩 에이전트가 릴리스를 깨지 않고 브랜치와 커밋 규칙을 지키게 하기
버전 관리되는 저장소에서 AI 코딩 에이전트에게 작업을 맡길 때 쓴다. 브랜치 만들기, 테스트, 커밋 작성, 임시 파일 위치, 여러 AI 도구에서 통하는 에이전트 지침 작성법을 정해 릴리스가 깨지지 않게 한다.
이 AI 규칙을 사용하려면
## 에이전트 지침 파일을 하나로 두고 저장소의 어느 부분을 고칠지 확인하기
- 에이전트 지침 파일은 정본 하나만 유지한다. 하니스가 다른 파일명을 요구하면 내용을 복제하지 말고 심볼릭 링크로 정본을 가리키게 한다. 실제 파일을 두 개로 나누면 시간이 지나며 내용이 어긋나고, 대체 경로를 별도 산출물 유형으로 취급하는 검증기가 오작동할 수 있다. 심볼릭 링크는 그대로 유지하고 일반 파일로 되돌리지 않는다.
- 하나의 변경이 여러 표면(배포되는 제품 콘텐츠, 카탈로그·설정 메타데이터, 빌드·CLI 코드)을 동시에 건드릴 수 있다. 수정한 각 파일이 어느 표면에 속하는지 확인하지 않은 채 "그냥 코드"나 "그냥 문서"라고 단정하지 말고, 인접한 표면이 아니라 실제 동작을 소유한 표면을 수정한다.
- 저장소 자체의 지침 파일은 그 저장소를 위한 작성 맥락으로 취급한다. 런타임에 배포된 산출물의 동작에 영향을 미쳐야 하는 규칙은 저장소 지침 파일이 아니라 해당 산출물 안에 넣는다. 최종 사용자는 자신의 지침으로 그 산출물을 실행하기 때문이다.
## 기능 브랜치에서 작업하고 풀 리퀘스트로만 머지하기
- 사소하지 않은 변경에는 기능 브랜치를 만든다. 이미 해당 작업에 맞는 브랜치에 있다면 그대로 사용하고, 명시적으로 요청받지 않는 한 브랜치나 워크트리를 추가로 만들지 않는다.
- 기본 브랜치로 가는 모든 변경은 풀 리퀘스트를 거치게 한다. 직접 푸시나 머지는 검증과 릴리스 점검을 건너뛰어 버전 불일치를 일으키고 나중에 여러 단계의 복구가 필요해진다. 이를 우회하지 말고 브랜치 보호 규칙에 맡겨 강제한다.
- 릴리스는 릴리스 자동화에 맡긴다. 일상적인 작업에서 릴리스가 관리하는 버전 번호를 손으로 올리거나 릴리스 노트를 직접 작성하지 말고, 자동화가 변경 의도를 분류할 수 있도록 컨벤셔널 커밋 제목을 사용한다.
## 변경 후 전체 테스트를 돌리고 자동 테스트와 평가를 나누기
- 파싱, 변환, 출력, 기계적 규칙에 영향을 주는 변경 후에는 전체 테스트 스위트를 실행한다. 로컬에서 도는 스위트는 CI가 실행하는 것과 동일하며, 건너뛸 수 있는 별도의 로컬 전용 트랙은 없다.
- 검사는 성격에 따라 나눈다. 스키마·프런트매터, 경로 안전성, 출력 형태, grep으로 확인 가능한 계약 같은 결정적 불변 조건은 자동화된 스위트에 두고 CI를 반드시 통과해야 한다. 판단이나 절제 같은 비결정적 동작은 평가로 검증하며, 깨지기 쉬운 문자열 테스트로 흉내 내지 않는다.
- 리뷰어가 스위트에서 빠진, grep으로 확인 가능한 불변 조건을 발견하면 새 스위트를 추가하기보다 기존 가드를 강화하는 쪽을 택한다. 파일 전체를 스냅샷으로 남기지 말고, 회귀를 일으킨 변경에서 실패했을 가장 작은 반증 가능한 단위(토큰 하나, 경로 하나, 픽스처 하나)를 고정한다.
## 임시 파일 위치를 정하고 계획과 문서는 저장소에 남기기
- 한 번 쓰고 버리는 임시 파일은 기본적으로 OS 임시 디렉터리로 보낸다. 실행마다 생기고 OS가 회수하는 임시 디렉터리로, 한 번 소비되고 버려지는 파일에 쓴다.
- 실행 간에 유지되어야 하는 임시 파일은 무작위 임시 디렉터리 대신 현재 사용자가 소유한 고정된 접두사를 사용해 이후 실행에서도 찾을 수 있게 한다. 루트 디렉터리는 비공개(소유자 전용)로 만들고, 심볼릭 링크이거나 다른 사용자 소유라면 거부한다. 이후 실행이 형제 최종 산출물을 의도적으로 나열하는 경우가 아니라면 실행당 디렉터리 하나를 유지한다.
- 임시 파일을 작업 저장소에 묶는 것은 그 산출물이 이 저장소나 브랜치와 정말로 떼어놓을 수 없거나, 사용자가 직접 정리할 용도이거나, 저장소 상대 경로 자체가 결과물의 핵심일 때만 한다.
- 계획서, 명세서, 문서, 최종 산출물 같은 영구 산출물은 저장소가 추적하는 위치에 두며, 어느 쪽이든 임시 계층에는 절대 두지 않는다.
## 이름은 ASCII만 쓰고 Markdown 표는 세로선으로 구분하기
- 파일명, 명령어, 에이전트 이름 같은 식별자는 ASCII만 사용한다. 변환기와 정규식 패턴이 이를 전제로 하기 때문이다.
- 마크다운 표는 파이프로 구분한 행으로 작성하고, 박스 그리기 문자는 절대 쓰지 않는다.
- 본문 텍스트에서는 유니코드를 사용해도 괜찮다. 다만 코드 블록과 터미널 예시 안에서는 ASCII 화살표(-> 및 <-)를 사용해 복사한 명령어가 이식성을 유지하게 한다.
## 변경 내용에 따라 커밋 접두어 fix와 feature를 고르기
- 컨벤셔널 커밋 접두사는 파일 유형이 아니라 변경이 실제로 하는 일에 따라 고른다. docs나 skills 디렉터리 아래에 있는 콘텐츠도 제품 코드일 수 있으므로, docs 접두사는 오직 문서화만을 목적으로 하는 파일에만 사용한다.
- fix와 feature 접두사가 둘 다 맞아 보일 때는 fix를 기본으로 삼는다. 코드를 추가해 구현했더라도 고장 나거나 빠진 동작을 고치는 것은 fix다. feature 접두사는 아무것도 고장 나지 않은 상태에서 사용자가 이전에는 할 수 없던 기능을 가능하게 할 때만 쓴다. 판단 기준은, 오늘 작성한 회귀 테스트가 이 변경 전이라면 실패했을지 여부다.
- 커밋 범위는 스킬, 모듈, 영역 이름처럼 의미 있는 가장 좁은 범위로 잡고, 프로젝트 전체를 범위로 삼지 않는다. 브레이킹 체인지 표시는 사용자의 명시적 확인 없이는 절대 추가하지 않는다. 사용자가 원하지 않을 수도 있는 메이저 버전 자동 상승을 유발하기 때문이다.
## 상대 경로로 각 배포 단위가 어떤 AI 도구에서도 돌아가게 하기
- 배포 가능한 각 단위는 자기 완결적으로 유지한다. 자신의 디렉터리 트리 안에 있는 파일만 상대 경로로 참조한다. 형제 단위로 넘어가거나 절대 설치 경로를 하드코딩하지 않는다. 설치 위치는 버전마다 달라지고 예측할 수 없으며, 패키징은 각 단위를 독립적으로 복사한다. 디렉터리를 넘나들며 의존하기보다 공유 파일은 각 단위에 복제해 둔다.
- skill-directory, session-id, plugin-root 같은 호스트별 변수는, 값이 비어 있거나 해석되지 않을 때 동작하는 대안 없이는 의존하지 않는다. 이런 변수 중 상당수는 특정 하니스 하나에서만 채워지고 다른 곳에서는 조용히 비어 있다.
- 번들된 스크립트를 실행하는 셸 명령에서는 경로 해석을 결정적으로 만든다. 같은 명령 안에 절대 기준 디렉터리를 인라인으로 설정하고(셸 상태는 호출 간에 유지되지 않는다) 거기서부터 스크립트를 참조한다. 단순한 상대 경로는 그 단위가 아니라 사용자의 프로젝트 디렉터리 기준으로 해석되므로 피한다.
## 에이전트 지침에는 확인 가능한 규칙만 쓰고 리뷰 의견을 분류한 뒤 고치기
- 한 줄을 지침에 넣는 것은 그것이 반증 가능한 제약을 명시하거나, 알려진 기본 경향이나 관찰된 지름길을 막거나, 판단을 바꾸는 도메인 지식을 제공할 때만 허용한다. "철저히 하라" 같은 막연한 노력·품질 표현은 없애고 관찰 가능한 규칙으로 바꾸거나 아예 지운다. 이미 그 자체로 성립하는 지시문에 동기부여용 근거를 덧붙이지 않는다.
- 지시를 반복하는 것은 그 위치가 지시의 발동 여부를 바꾼다고 입증된 이탈 지점에서만 한다. 정말로 중복이 필요한 경우에는 패리티 테스트로 그 중복을 보호한다.
- 리뷰 피드백을 반영하는 일은 실질적인 개정으로 취급한다. 각 항목을 Change, Verify, Consider로 분류하고 Change 항목만 수정한다. 각 항목은 그것을 소유한 계층에서 가장 작은 방식으로 고치고, 수정 후에는 해당 블록을 다시 읽어 그 변경으로 중복되거나 낡아진 텍스트를 제거한다. 리뷰어의 표현은 수정에 대한 하나의 가설일 뿐, 수정 내용을 결정하는 권한이 아니다.
## 지침 파일 이름 대신 이미 불러온 맥락에서 프로젝트 규칙 찾기
- 어떤 단위가 이슈 트래커, 커밋 형식, 린트 명령, 범위 제한 같은 프로젝트 컨벤션을 알아내야 할 때는, 어떤 파일을 열어야 하는지가 아니라 에이전트가 이미 불러온 맥락에서 무엇을 찾아야 하는지를 설명한다. 읽기 경로에서는 지침용 닷파일 이름을 지정하지 않는다. 호스트가 이미 루트 지침 파일을 주입하고 있고, 그 파일명은 하니스마다 다르며, 에이전트에게 이름으로 지침 파일을 열라고 지시하는 것은 프롬프트 인젝션 방어 체계가 정확히 경계하는 형태이기 때문이다.
- 구체적인 파일명을 지정하는 것은 맥락 참조만으로는 필요를 표현할 수 없는 경우, 즉 컨벤션을 대상에 다시 써넣거나 새로 시작한 서브에이전트가 정말로 물려받지 못한 내용을 읽어야 할 때뿐이다.
- 기능은 범주로 설명한다("프로젝트의 이슈 트래커, 예를 들어 GitHub Issues, Linear, Jira"와 "그것이 제공하는 어떤 인터페이스든"). 바이너리, 환경 변수, 커넥터가 없다고 해서 그 기능을 쓸 수 없다는 증거로 취급하지 않는다.
## 버전 번호가 아닌 내용을 비교해 세션이 최신 수정을 읽었는지 확인하기
- 장시간 실행 중인 세션이 시작 시 불러온 내용을 수정한 뒤에는, 그 세션이 여전히 수정 전 사본을 실행하고 있다고 가정한다. 캐시는 로드 시점의 상태를 담고, 릴리스 내에서의 수정은 버전 번호를 바꾸지 않는다. 따라서 버전이 일치한다는 것은 캐시가 이 릴리스에서 왔다는 증거일 뿐, 최신 수정을 반영했다는 증거는 아니다.
- 어느 사본이 실제로 살아 있는지는 버전 문자열을 읽는 대신 불러온 파일의 내용을 작업 사본과 직접 비교해 확인한다. 내용이 같으면 실행 중인 사본이 최신이고 신뢰할 수 있다는 뜻이고, 다르면 동작 테스트를 신뢰하기 전에 재시작하거나 새로 로드하는 경로를 사용해야 한다는 뜻이다.
- 스크립트, 파서 등 테스트 스위트가 실행하는 기계적 코드 경로는 항상 최신 소스로 실행된다. 이런 낡은 상태 문제는 시작 시 불러오는 지침·본문 계층에만 해당한다.
