--- name: vibe-plan description: 접수된 요청을 트리아지한다. 용어집과 아키텍처 결정 기록(Architecture Decision Record, ADR)을 문서에 남긴다. 인터뷰로 요청을 구체화한다. 그 뒤 명세와 tracer-bullet 티켓을 작성한다. 이 스킬은 이슈와 외부 PR의 트리아지, 기능 계획, 명세, 제품 요구사항 문서(Product Requirements Document, PRD), 티켓 작성에 쓰인다. 명시적 plan/review-plan/reconcile 또는 audit/next의 확정된 선택을 받으면 자기완결 실행계획의 작성·보완·상태 조정을 담당한다. disable-model-invocation: true --- # 작업 계획하기 요청이나 느슨한 아이디어를 네 단계로 구체화해 에이전트가 바로 구현할 수 있는 티켓을 만든다. - **트리아지**: 들어온 외부 요청을 판단한다. 이 단계에서 실행을 끝낼 수도 있다. - **그릴**: 인터뷰로 요청을 구체화한다. 결정이 확정되면 문서에 기록해 둔다. - **명세**: 확정한 결정을 명세로 합성한다. - **티켓**: 작업을 tracer-bullet 방식으로 나눈다. 각 티켓은 세로 슬라이스 하나를 맡는다. 각 티켓에는 선행 작업을 기록한다. ## 실행계획 분기 먼저 확인 명시적 `/vibe-plan plan `, `review-plan `, `reconcile` 또는 `/vibe-audit`·`/vibe-next-plan`에서 확정된 선택을 인계받으면 [EXECUTION-PLAN.md](EXECUTION-PLAN.md)를 읽고 그 경로만 수행한다. 선택이 미확정인 조사 목록은 먼저 그 문서의 선택 게이트를 적용한다. 단순히 "계획"이라는 단어가 있거나 기존 명세·티켓·결정 지도를 받았다는 이유로 실행계획으로 바꾸지 않는다. 이 분기에서는 계획 파일과 색인만 변경하며, `--issues`가 명시된 경우에만 그 문서의 GitHub 게시 절차를 적용한다. 아래 Stage 0-3의 모델링 문서 자동 수정, commit과 push, 일반 티켓 게시 규칙은 적용하지 않는다. 실행은 `/vibe-implement execute `에 맡긴다. 이 분기 외에는 기존 Stage 0-3을 그대로 따른다. [설정 문서와 기본값](../vibe-init/references/defaults.md)을 읽고 트래커·라벨·도메인 문서 규칙을 해석한다. 설정이 없으면 초기화를 요구하지 않고 로컬 Markdown으로 진행한다. ## 올바른 단계로 진입 **사용자가 가져온 것에서 추론할 수 있는 내용은 묻지 않는다.** 인자와 대화를 읽은 뒤, 입력이 아직 없는 첫 단계로 진입한다: - 외부 요청: 사용자가 직접 작성하지 않은 버그 신고나 기능 요청, 이슈 링크나 번호, 외부 PR이면 **Stage 0: 트리아지**로 진입한다. - 느슨한 아이디어나 열린 질문이 있는 계획이면 **Stage 1: 그릴**로 진입한다. - 정리된 대화에 열린 결정이 없으면 **Stage 2: 명세**로 진입한다. - 사용자가 작성한 기존 명세, PRD, 계획, 이슈 번호나 URL이면 **Stage 3: 티켓**으로 진입한다. **출처가 사용자가 아니면 외부로 취급한다.** 단독 링크나 이슈 번호, 복사해 붙인 신고, "누가 올렸다", 협업자의 PR처럼 이 저장소의 계획에서 사용자와 직접 만들지 않은 내용은 모두 먼저 Stage 0을 지난다. 이 스킬이 게시한 티켓은 구조상 이미 `ready-for-agent`다: **자기 산출물을 절대 트리아지하지 않는다.** 모호하다면 어느 쪽으로 읽었는지 말한다. 사용자가 한마디로 바로잡을 수 있다. **결정 지도는 요청이 아니다.** `상태:초안` 라벨이 붙은 이슈, 또는 그 `유형:조사` / `유형:프로토타입` / `유형:인터뷰` / `유형:작업` 결정 티켓은 `/vibe-deep-plan`에 속하며, 진행 중인 계획 산물이므로 트리아지 대상이 아니다. `유형:계획`은 이 스킬이 게시한 명세와 계획 이슈를 뜻하므로 결정 티켓으로 분류하지 않는다. 사용자가 결정 지도나 그 티켓을 가리키면 그렇게 말하고 대신 `/vibe-deep-plan`을 제안한다. 그런 지도가 정리되면, 그 Destination과 `Decisions so far`는 **Stage 2: 명세**의 유효한 입력이다. 실제 결정 티켓 문자열은 `docs/agents/triage-labels.md`에서 푼다. `status:draft` 같은 영어 대역을 만들지 않는다. Stage 3을 참조(명세 경로, 이슈 번호나 URL)와 함께 진입할 때, 가져와서 본문과 댓글 전체를 읽는다. 원본이 이미 기록한 결정을 다시 인터뷰하지 않고, 이미 존재하는 명세를 다시 합성하지 않는다. 이 진입 규칙의 요점은 앞으로 건너뛰는 것이다. 어느 단계로 진입하는지 그리고 왜인지 한 줄로 말한 뒤 시작한다. 로컬 계획 문서를 바꿀 수 있는 단계에 들어가기 전에, `git status`, 현재 브랜치, upstream과의 차이, 이미 push되지 않은 commit을 기록한다. 이후 이 계획의 변경과 기존 사용자 작업을 구분하는 기준으로 쓴다. ## 맥락 위생 실행 단계를 **하나의 끊기지 않는 맥락 창**에 유지한다. 티켓을 게시할 때까지 맥락을 압축하거나 비우지 않는다. 그러면 트리아지부터 티켓까지 각 단계가 앞 단계의 결과를 그대로 이어받는다. 에이전트는 티켓마다 새 세션에서 해당 티켓만을 바탕으로 구현한다. 티켓을 게시하기 전에 세션의 응답 품질이 떨어지면 작업을 멈춘다. 사용자에게 `/vibe-handoff`를 실행하고 새 세션에서 이어가도록 요청한다. ## 산출물 언어 실행계획도 저장소 문서와 같은 언어로 작성한다. 기계가 읽는 키와 리터럴은 번역하지 않는다. 그 예는 `Kind`, `Status`, `Planned at`, `Depends on`이다. 파일 경로와 코드 발췌 제한은 일반 명세, 티켓, 에이전트 브리프에만 적용한다. 실행계획에는 계획을 쓰는 시점의 코드에서 정확한 경로와 발췌를 포함한다. 아래 템플릿의 헤딩은 자리표시자이며 그대로 출력할 문구가 아니다. 게시하는 모든 산출물(제목, 헤딩, 본문)을 이 저장소 문서가 쓰는 언어로 쓰고, 새로 번역하기보다 기존 이슈에 이미 쓰인 헤딩 어휘를 재사용한다. 본문은 번역됐는데 헤딩만 영어인 혼합 출력은 템플릿을 그대로 복사했다는 신호다. --- ## Stage 0: 트리아지 트리아지는 들어온 요청을 상태 전이 규칙에 따라 처리한다. 이 단계에서 실행을 끝낼 수 있다. `wontfix`와 `needs-info`는 실패가 아니라 종료 결과다. `ready-for-agent` 또는 `ready-for-human`에 도달한 요청만 다음 단계로 이어진다. 이어지는 단계는 트리아지에서 확정한 내용에 따라 정한다. - 완전히 명세화됐고 결정할 게 없으면: **Stage 3: 티켓**으로 진입한다. 티켓 하나로 충분하면 그 이슈를 그대로 둔다. - 트리아지 뒤에도 결정이 열려 있으면: 트리아지 노트를 앞으로 가져가며 **Stage 1: 그릴**로 진입한다. 이슈 트래커 설정의 `PR을 요청 표면으로` 플래그가 켜져 있으면 외부 PR을 트리아지에 포함한다. **PR은 코드가 딸린 이슈다.** 이슈와 같은 역할과 상태 규칙을 적용한다. 아래에서 "PR의 경우"라고 표시한 내용만 다르게 처리한다. 단독 `#42`는 트래커 설정에 따라 이슈나 PR로 식별한다. 트리아지 중 이슈 트래커에 게시하는 모든 댓글과 이슈는 반드시 이 고지문으로 시작한다: ``` > *이 내용은 트리아지 중 AI가 생성했습니다.* ``` 이 단계의 참고 문서: - [AGENT-BRIEF.md](AGENT-BRIEF.md): 내구성 있는 에이전트 브리프를 쓰는 법 - [OUT-OF-SCOPE.md](OUT-OF-SCOPE.md): `docs/agents/out-of-scope/` 지식 베이스가 어떻게 동작하는가 ### 역할 두 **범주** 역할: - `bug`: 무언가 망가졌다 - `enhancement`: 새 기능 또는 개선 다섯 **상태** 역할: - `needs-triage`: 관리자가 평가해야 한다 - `needs-info`: 신고자의 추가 정보를 기다리는 중 - `ready-for-agent`: 완전히 명세화됐고, 사람이 지켜보지 않아도 작업하는 에이전트(Away From Keyboard, AFK)가 가져갈 준비가 됨 - `ready-for-human`: 사람이 구현해야 한다 - `wontfix`: 조치하지 않는다 PR에서는 같은 상태 역할을 첨부한 코드에 적용한다. `ready-for-agent`는 브리프가 첨부되어 에이전트가 diff에서 다음 단계를 밟아야 한다는 뜻이다. `ready-for-human`은 사람이 코드를 병합할 준비가 됐다는 뜻이다. 트리아지된 모든 이슈는 정확히 하나의 범주 역할과 하나의 상태 역할을 가져야 한다. 상태 역할이 충돌하면, 그것을 표시하고 다른 일을 하기 전에 관리자에게 묻는다. 위 이름은 범주 역할과 상태 역할의 기준 이름이다. 이슈 트래커에서는 실제 라벨 문자열을 다르게 쓸 수 있다. 실제 문자열은 `docs/agents/triage-labels.md`에서 확인한다. 상태 전이는 다음과 같다. 라벨이 없는 이슈에는 보통 먼저 `needs-triage`를 적용한다. 그 뒤 이슈 상태를 `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix` 중 하나로 바꾼다. 신고자가 `needs-info` 이슈에 답하면 `needs-triage`로 돌린다. 관리자는 언제든 상태를 바꿀 수 있다. 흔하지 않은 전이는 표시하고 진행하기 전에 관리자에게 묻는다. ### 호출 관리자가 요청한 것을 해석하고 바로 실행한다. 예: - "주의가 필요한 것을 보여줘" - "#42를 보자"(이슈 또는 PR) - "#42를 ready-for-agent로 옮겨줘" - "에이전트가 집어갈 준비가 된 건 뭐야?" ### 주의가 필요한 것 보여주기 이슈 트래커를 조회해 세 묶음을 오래된 항목부터 제시한다: 1. **라벨 없음**: 트리아지된 적 없음. 2. **`needs-triage`**: 평가 진행 중. 3. **마지막 트리아지 노트 이후 신고자 활동이 있는 `needs-info`**: 재평가 필요. PR이 범위 안이면 이 묶음에 외부 PR을 포함하고 각 줄에 `[PR]` 또는 `[issue]`로 표시한다. 발견 결과에는 *외부* PR만 표시한다. 외부인 여부는 트래커 설정이 정의한다. 협업자의 진행 중 PR은 트리아지 작업이 아니다. 이 필터는 발견에만 쓴다. 명시적으로 지정된 PR은 작성자와 무관하게 항상 트리아지한다. 개수와 항목당 한 줄 요약을 보여준다. 관리자가 고르게 한다. ### 특정 이슈 또는 PR 트리아지 1. **맥락 수집.** - 이슈 또는 PR 전체를 읽는다. 본문, 댓글, 라벨, 작성자와 날짜를 확인한다. PR이면 diff도 읽는다. - 이전 트리아지 노트가 있으면 읽고 해결된 질문을 확인한다. 이미 해결된 질문은 다시 묻지 않는다. - 프로젝트의 도메인 용어집을 써서 코드베이스를 탐색하고 그 영역의 ADR을 따른다. - 요청의 표현이 아니라 도메인 개념을 기준으로 기존 구현을 찾는다. 발견 위치를 관리자에게 보고한다. - 기존 구현이 있으면 이미 구현된 `wontfix`로 분류한다(5단계). - `docs/agents/out-of-scope/*.md` 파일을 읽고 이 요청과 비슷한 거부 기록을 관리자에게 알린다. 2. **권고.** 관리자에게 범주와 상태를 권고하고 이유를 설명한다. 요청한 동작이 코드베이스에 이미 구현되었는지 포함한 요약도 함께 전한다. 지시를 기다린다. 3. **주장 검증.** 그릴 전에 주장이 성립하는지 확인한다. 버그면 신고자의 단계를 따라 재현한다. **외부 PR의 경우:** - **정적 검토 먼저.** checkout이나 명령 실행 전에 에이전트는 diff가 바꾼 영역의 저장소 맥락을 확인한다. 에이전트는 모든 핵심 PR 주장에 정적 근거를 대조해 뒷받침됨, 반박됨, 미검증으로 나눈다. 각 미검증 주장에 필요한 런타임 증거를 정한다. - **제안하고, 그 뒤 기다린다.** 트리아지 권고에 필요한 핵심 미검증 주장을 해결하려면 명령 실행이 필요할 때만 실행을 제안한다. 이유, 정확한 명령, 예상 부작용, 샌드박스 보장(일회용, 비밀 없음, 쓰기 자격증명 없음, 기본 네트워크 차단)을 먼저 보여준다. 별도의 명시적 승인을 기다린다. PR 트리아지나 테스트를 요청한 초기 지시는 승인이 아니다. - **보안 샌드박스에서만 실행.** 승인받은 뒤, 보안 보장을 갖춘 일회용 샌드박스에서만 신뢰할 수 없는 코드를 실행한다. 전용 worktree는 checkout 격리만 제공한다. 전용 worktree는 보안 샌드박스가 아니며 목표별 통합 작업공간도 구성하지 않는다. 전용 worktree만으로 보안 샌드박스 요구를 충족했다고 보지 않는다. - **안전하게 멈춘다.** 조건에 맞는 샌드박스가 없거나 승인을 받지 못했거나 거부되면 checkout하거나 아무것도 실행하지 않는다. 정적 검토를 마친다. 미검증 주장과 검증 한계를 명시한다. - 검증 결과를 다음 네 가지 중 하나로 보고한다: - 코드 경로나 실행 증거를 제시해 확인했다. - 검증에 실패했다. - 정적 근거는 있지만 런타임 검증은 하지 않았다. - 세부 정보가 부족하다(강한 `needs-info` 신호). - 확인된 검증은 훨씬 강한 에이전트 브리프를 만든다. 4. **그릴(필요 시).** 요청을 구체화해야 하면 지금 **Stage 1: 그릴**을 실행한다. 그릴에서 해결한 모든 내용을 브리프나 트리아지 노트에 기록한다. 5. **결과 적용.** - `ready-for-agent`: 에이전트 브리프 댓글 게시([AGENT-BRIEF.md](AGENT-BRIEF.md)). - `ready-for-human`: 에이전트 브리프와 같은 구조로 쓴다. 위임할 수 없는 이유(판단, 외부 접근, 설계 결정, 수동 테스트)를 적는다. - `needs-info`: 트리아지 노트를 게시한다(아래 템플릿). - `wontfix`: 닫고, 댓글은 *왜*에 따라 다르다: - **이미 구현됨**: 변경이 코드베이스에 이미 있다. 그 위치를 가리킨다. `docs/agents/out-of-scope/`에는 쓰지 **않는다**. 그 지식 기반은 거부한 요청만 다루며, 이미 구현된 요청은 기록하지 않는다. - **거부(버그)**: 정중하게 설명한 뒤 닫는다. - **거부(기능 요청)**: `docs/agents/out-of-scope/`에 기록하고 댓글에서 연결한 뒤 닫는다([OUT-OF-SCOPE.md](OUT-OF-SCOPE.md)). - `needs-triage`: 역할을 적용한다. 부분 진척이 있으면 댓글은 선택이다. ### 빠른 상태 덮어쓰기 관리자가 "#42를 ready-for-agent로 옮겨줘"라고 하면, 신뢰하고 역할을 직접 적용한다. 무엇을 하려는지(역할 변경, 댓글, 닫기) 확인한 뒤 실행한다. 그릴을 건너뛴다. 그릴 세션 없이 `ready-for-agent`로 옮기면, 에이전트 브리프를 쓸지 묻는다. ### Needs-info 템플릿 ```markdown ## 트리아지 노트 **지금까지 확정된 것:** - 요점 1 - 요점 2 **신고자가 답해야 할 질문 (@reporter):** - 질문 1 - 질문 2 ``` 그릴 중 해결된 모든 것을 "지금까지 확정된 것" 아래 잡아 작업이 사라지지 않게 한다. 질문은 구체적이고 실행 가능해야 한다, "추가 정보를 제공해 주세요"가 아니다. ### 이전 세션 재개 이슈나 PR에 이전 트리아지 노트가 있으면, 읽고, 신고자가 남은 질문에 답했는지 확인하고, 계속하기 전에 갱신된 그림을 제시한다. 해결된 질문을 다시 묻지 않는다. --- ## Stage 1: 그릴 Stage 1에서는 `vibe-grilling`과 `vibe-modeling` 스킬을 각각 로드해 적용한다. 한 번에 한 질문씩 인터뷰한다. 공유 이해에 도달할 때까지 인터뷰를 이어간다. 결정이 확정되면 도메인 용어의 뜻을 구체화한다. 그 직후 `CONTEXT.md`와 ADR을 갱신한다. 사용자가 공유 이해를 확인하기 전까지 Stage 2로 넘어가지 않는다. --- ## Stage 2: 명세 이미 결정한 내용을 명세로 합성한다. 이 문서는 PRD라고도 부른다. 여기서 사용자를 인터뷰하지 않는다. 질문은 Stage 1에서 다루고, 이 단계에서는 합성만 한다. Stage 2가 `/vibe-deep-plan`에서 정리된 결정 지도를 받으면, 명세를 합성하기 전에 지도를 읽는다. 지도가 연결한 해결된 기록이나 티켓 링크를 모두 따른다. 먼저 연결된 각 결정의 질문과 최종 답을 읽는다. 로컬 마크다운이면 `## Question`과 `## Answer`를, 호스트 트래커면 이슈의 질문이나 본문과 최종 결정 댓글 또는 노트를 읽는다. 미가공 `## Research`, 댓글, 첨부, 프로토타입 산물은 최종 답이 참조하거나 명세에 증거가 필요할 때만 따른다. 지도는 연결된 결정을 대체하지 않고 색인으로 쓴다. 로컬 마크다운에서 지도가 `.agents/plans//map.md`이면, 기존 `.agents/plans//`를 재사용해 `spec.md`를 쓴다. 새 `` 디렉터리는 만들지 않는다. 호스트 트래커는 주어진 지도 URL이나 번호와 설정된 명세 게시 위치를 쓴다. ### 로컬 결정 게이트 Stage 2로 어떻게 진입했든, 명세를 쓰기 전에 `vibe-modeling` 스킬을 로드해 그 기준으로 확정된 결정을 확인한다. 이미 끝난 결정을 다시 인터뷰하지 않는다. 새 도메인 용어는 `CONTEXT.md`에 기록하고, ADR 세 조건을 만족하지만 아직 기록되지 않은 결정은 ADR로 쓴다. 같은 결정을 다시 기록하지 않는다. 호스트형 트래커에 명세, 라벨, 티켓을 처음 게시하거나 수정하기 전에 이번 계획에서 바꾼 `CONTEXT.md`와 ADR을 commit하고 현재 브랜치를 일반 push한다. `git add .`을 쓰지 않고 이번 실행의 정확한 경로만 stage한다. 기존 사용자 변경을 섞지 않는다. staged diff와 commit SHA를 확인한다. push 뒤 원격 브랜치에 그 SHA가 있는지 확인한다. force push는 하지 않는다. 시작할 때 push되지 않은 기존 commit이 있었다면 함께 게시될 commit을 보여 주고 먼저 사용자 승인을 받는다. 기존 변경을 안전하게 분리할 수 없거나 새 commit 생성, push, 원격 SHA 확인에 실패하면 작업을 중단한다. 이 경우 원격 트래커를 변경하지 않는다. 로컬 모델링 문서가 바뀌지 않았으면 새 commit이나 push를 만들지 않는다. 1. 아직 저장소를 탐색하지 않았다면 코드베이스의 현재 상태를 이해하려고 저장소를 탐색한다. 2. 명세 전체에 프로젝트의 도메인 용어집 어휘를 쓴다. 3. 손대는 영역의 ADR을 존중한다. 4. 기능을 테스트할 경계를 스케치한다. 기존 경계를 새 경계보다 우선한다. 가능한 한 바깥 경계를 쓴다. 새 경계가 필요하면 가능한 한 바깥에서 제안한다. 코드베이스에 걸친 경계가 적을수록 좋다. 이상적인 수는 하나다. 이 경계가 기대와 맞는지 사용자에게 확인한다. 5. 아래 템플릿으로 명세를 쓴다. 6. 명세를 프로젝트 이슈 트래커에 게시한다. 7. 호스트형 명세와 계획 이슈에는 `유형:계획` 라벨과 `ready-for-agent` 상태 역할 라벨을 함께 붙인다. 8. 호스트형 이슈에 추가 트리아지를 하지 않는다. 9. 로컬 Markdown 명세에는 호스트형 라벨을 적용하지 않는다. ## 문제 정의 사용자가 겪는 문제를 사용자 관점에서 쓴다. ## 해결 방안 문제를 해결하는 방안을 사용자 관점에서 쓴다. ## 사용자 스토리 사용자 스토리를 번호가 있는 긴 목록으로 작성한다. 각 사용자 스토리는 다음 형식을 따른다: 1. <행위자>로서, <기능>을 원한다, 왜냐하면 <이득> 1. 모바일 뱅킹 고객으로서, 지출을 더 잘 판단하려고 계좌 잔액을 보고 싶다. 이 사용자 스토리 목록은 매우 광범위해야 하고 기능의 모든 측면을 덮어야 한다. ## 구현 결정사항 내린 구현 결정을 목록으로 작성한다. 목록에는 다음 항목을 포함할 수 있다: - 구축하거나 수정할 모듈 - 수정될 그 모듈의 인터페이스 - 개발자의 기술적 명확화 - 아키텍처 결정 - 스키마 변경 - API 계약 - 구체적인 상호작용 구체적인 파일 경로나 코드 조각을 포함하지 **않는다**. 빠르게 시대에 뒤떨어질 수 있다. 예외: 프로토타입이 산문보다 더 정확하게 결정을 부호화한 조각(상태 기계, 리듀서, 스키마, 타입 형태)을 만들었다면, 해당 결정 안에 인라인하고 프로토타입에서 왔다고 간략히 적는다. 결정이 풍부한 부분만 남긴다: 작동하는 데모가 아니라 중요한 부분만. ## 테스트 결정사항 내린 테스트 결정을 목록으로 작성한다. 목록에는 다음 항목을 포함한다: - 좋은 테스트의 조건(구현 세부가 아니라 외부 동작만 테스트) - 테스트할 모듈 - 테스트의 선례(코드베이스에 있는 비슷한 테스트) ## 범위 밖 이 명세에서 범위 밖인 내용을 설명한다. ## 추가 참고사항 기능을 설명하는 추가 메모를 쓴다. --- ## Stage 3: 티켓 명세(또는 진입하면서 가져온 계획이나 대화)를 **티켓**으로 나눈다. 각 티켓은 tracer-bullet 방식으로 세로 슬라이스 하나를 맡는다. 각 티켓에 선행 작업을 지정한다. 초안 작성 전에, 아직 안 했다면 코드베이스를 탐색한다. 티켓 제목과 설명은 프로젝트의 도메인 용어집 어휘를 써야 하고, 손대는 영역의 ADR을 존중해야 한다. 구현을 쉽게 하려고 먼저 리팩터링할 코드를 찾는다. "변경을 쉽게 만든 다음, 쉬운 변경을 한다." ### 1. 세로 슬라이스 초안 작업을 **tracer bullet** 방식의 티켓으로 나눈다. 각 티켓은 한 세로 슬라이스를 구현한다. - 각 슬라이스는 모든 계층(스키마, API, UI, 테스트)을 가로지르는 좁지만 완전한 경로를 자른다. 한 계층의 가로 슬라이스가 아니라 세로 슬라이스다 - 완성된 슬라이스는 자체적으로 데모하거나 검증할 수 있다 - 각 슬라이스는 하나의 새 맥락 창에 들어가도록 크기를 잡는다 - 사전 리팩터링이 있으면 먼저 한다 한 티켓이 다른 티켓의 완료를 기다리는 관계를 **차단 간선**이라고 부른다. 각 티켓에 선행 작업을 지정한다. 선행 작업이 없는 티켓은 바로 시작할 수 있다. **넓은 리팩터링은 세로 슬라이스의 예외다.** 컬럼 이름이나 공유 심볼의 타입을 바꾸는 작업이 그 예다. 한 번의 기계적 변경으로 **blast radius**(영향 범위)가 코드베이스 전체에 미칠 수 있다. 단일 편집이 수천 호출 지점을 깨뜨릴 수 있다. 세로 슬라이스 하나로는 이 변경의 빌드를 통과시킬 수 없다. 이 변경을 tracer bullet에 억지로 맞추지 않는다. **expand–contract** 방식을 적용한다. 먼저 새 형태를 기존 형태 옆에 추가해 기존 호출자를 깨뜨리지 않는다. 그 뒤 blast radius 단위인 패키지나 디렉터리별로 호출 지점을 묶어 옮긴다. 각 묶음을 새 형태 추가 작업에 의존하는 별도 티켓으로 만든다. 옛 형태가 남아 있는 동안 각 묶음의 CI를 통과시킨다. 마지막으로 남은 호출자가 없으면 옛 형태를 지운다. 삭제 티켓은 모든 옮김 티켓을 선행 작업으로 둔다. 각 묶음이 단독으로 CI를 통과하지 못하면 에이전트는 모든 묶음을 같은 통합 브랜치에서 순서대로 처리한다. 그 브랜치에는 모든 묶음에 의존하는 최종 통합 검증 티켓을 둔다. CI 통과는 그 티켓에서만 약속한다. ### 2. 사용자에게 확인 제안된 분해를 번호 목록으로 제시한다. 각 티켓에: - **제목**: 짧은 설명형 이름 - **선행 작업**: (있으면) 먼저 완료되어야 할 다른 티켓 - **전달하는 것**: 이 티켓이 작동하게 만드는 엔드투엔드 동작 사용자에게 묻는다: - 세분성이 맞는가? (너무 크다 / 너무 잘다) - 차단 간선이 맞는가(각 티켓이 정말로 자신을 가로막는 티켓에만 의존하는가)? - 병합하거나 더 쪼갤 티켓이 있는가? 사용자가 분해를 승인할 때까지 반복한다. ### 3. 설정된 트래커에 티켓 게시 승인된 티켓을 선행 작업 순서대로 게시해 각 티켓의 차단 간선이 실제 식별자를 참조하게 한다. 게시 방식은 해석된 트래커 설정에 따른다. 트래커를 설정하지 않았으면 로컬 Markdown을 기본값으로 쓴다. 티켓 내용은 로컬이나 원격에서 같고, 부모 링크와 차단 간선의 형태만 다르다. 달리 지시받지 않으면 트리아지에 쓰는 `ready-for-agent` 상태 역할을 적용한다. 실제 문자열은 `docs/agents/triage-labels.md` 또는 내장 기본 매핑에서 확인한다. 로컬에서는 Markdown에 기록하고 원격 라벨을 만들지 않는다. 정리된 로컬 마크다운 지도의 경우, 지도의 기존 `` 디렉터리를 구현 티켓의 출력 루트로 유지한다. Stage 3에서 두 번째 기능 디렉터리를 만들지 않고 지도의 `spec.md`를 다시 쓰지 않는다. **호스트형 트래커 + 티켓 하나.** 승인된 분해가 정확히 한 장이고 트래커가 GitHub·GitLab·그 외 원격이면, 명세와 구현 계획을 나누지 않는다. 자식(sub-issue / task / 링크된 이슈)을 만들지 않는다. 명세 이슈 본문에 **구현할 내용**과 **완료 조건**을 합친다: 그 이슈가 명세이자 유일한 티켓이다. Stage 2가 이미 명세를 게시했으면 그 본문을 갱신하고 두 번째 이슈를 만들지 않으며, 기존 `유형:계획`과 `ready-for-agent` 라벨을 유지한다. 로컬 마크다운은 이 통합을 쓰지 않는다: `spec.md`와 `issues/01-*.md`를 계속 따로 둔다. - **로컬 파일:** - 입력이 정리된 지도 `.agents/plans//map.md`이면 같은 `.agents/plans//issues/-.md`에 티켓 파일을 만든다. - 입력이 `.agents/plans//spec.md`의 명세이면 같은 effort의 `issues/`에 기록한다. - 그 밖의 로컬 명세 경로를 입력으로 받으면 `.agents/plans//issues/-.md`에 티켓을 저장한다. - 티켓을 의존성 순서대로 `01`부터 번호를 매긴다. 선행 작업을 먼저 둔다. - 각 파일의 "선행 작업"에는 의존하는 티켓의 번호와 제목을 나열한다. - 아래의 티켓별 파일 템플릿을 쓴다. - 티켓마다 파일 하나만 만들고, 둘 이상의 티켓을 한 파일에 합치지 않는다. - **GitHub**: 티켓이 둘 이상이면 각 티켓을 명세 이슈의 **sub-issue**로 만든다. 부모가 진행을 렌더하고, 사용자는 UI에서 자식 이슈를 탐색할 수 있다. 먼저 이슈를 만들고(`gg issue create`), 그 뒤 `gg api --method POST repos///issues//sub_issues -F sub_issue_id=`로 연결한다, 여기서 ``는 자식의 숫자 **database id**다(`gg api repos///issues/ --jq .id`, `#number`나 `node_id`가 아니다). sub-issues가 활성화되지 않은 곳에서는 부모 본문의 작업 목록과 각 자식의 맨 위 `상위 이슈: #`로 폴백한다. 차단 간선에는 GitHub의 **네이티브 이슈 의존성**을 쓴다: `gg api --method POST repos///issues//dependencies/blocked_by -F issue_id=`. - **GitLab**: 티켓이 둘 이상이면 각 티켓을 명세 이슈의 **task**(GitLab의 자식 work item 타입)로 만든다. 부모 이슈는 일반 텍스트가 아닌 실제 work item 체크리스트로 진행 상황을 추적한다. 구조를 만들 때 **퀵 액션을 쓰지 않는다**. GitLab에는 `/parent`가 없고, `/set_parent`와 `/blocked_by`는 인스턴스에 따라 적용되지 않는다. 인식되지 않은 퀵 액션은 실패하지 않고 본문을 그대로 댓글로 게시한다. 아래 API 경로만 쓴다: - **생성**: `gg api --method POST "projects/:id/issues" -F "title=<제목>" -F "issue_type=task" -F "description=<본문>"`. - **부모 연결**: GraphQL로 자식과 부모의 work item global ID를 얻고(`workItems { nodes { id iid } }`), `workItemUpdate(input: { id: , hierarchyWidget: { parentId: } })`를 호출한다. - **차단 간선**: `gg api --method POST "projects/:id/issues//links" -F target_project_id= -F target_issue_iid= -F link_type=is_blocked_by`를 한 번 시도한다. `HTTP 400 link_type does not have a valid value`면 네이티브 차단 기능이 없는 Free 또는 CE 등급이므로, 차단 관계는 티켓 본문의 "선행 작업" 섹션에만 남긴다. 설명 맨 위에 `선행 작업:` 같은 줄을 따로 덧붙이지 않는다. 섹션과 중복된다. - **검증**: 게시 후 계층과 링크를 다시 질의해 확인하고, `/`로 시작하는 댓글이 남았으면 그 티켓은 구조 설정이 실패한 것이다. - **다른 트래커(Linear, Jira, …)**: 티켓이 둘 이상이면 티켓마다 이슈 하나를 게시한다. 플랫폼의 네이티브 부모-자식 관계를 쓴다. 네이티브 차단 관계가 있으면 그 관계를 사용한다. 없으면 각 티켓의 "선행 작업"에 의존하는 티켓을 적는다. **프론티어**는 선행 작업이 모두 끝난 티켓이다. 순수 선형 사슬이면 위에서 아래로 작업한다. 부모 이슈를 닫지 **않는다**. 부모 이슈를 수정하지 않는다(위 단일 티켓 통합만 예외다). # : <티켓 제목> **구현할 내용:** 이 티켓이 작동하게 만드는 엔드투엔드 동작을 사용자 관점에서 쓴다. 계층별 구현 목록은 아니다. **선행 작업:** 이 티켓이 의존하는 티켓의 번호와 제목을 적는다. 의존하는 티켓이 없으면 "없음: 즉시 시작 가능"이라고 적는다. **상태:** ready-for-agent - [ ] 완료 조건 1 - [ ] 완료 조건 2 ## 상위 이슈 트래커의 부모 이슈를 가리키는 참조를 적는다. 티켓이 이미 네이티브로 연결된 경우 이 섹션을 생략한다. GitHub sub-issue나 GitLab task는 트래커 자체에 부모 링크를 담으므로, 본문에 다시 적으면 중복이다. 네이티브 관계를 쓸 수 없을 때만 이 참조를 유지한다. ## 구현할 내용 이 티켓이 작동하게 만드는 엔드투엔드 동작을 사용자 관점에서 쓴다. 계층별 구현 목록은 아니다. ## 완료 조건 - [ ] 조건 1 - [ ] 조건 2 ## 선행 작업 - 각 선행 작업 티켓을 가리키는 참조, 또는 "없음: 즉시 시작 가능". 어느 형태든, 구체적인 파일 경로나 코드 조각을 피한다. 빠르게 시대에 뒤떨어지기 때문이다. 예외: 프로토타입이 산문보다 더 정확하게 결정을 부호화한 조각(상태 기계, 리듀서, 스키마, 타입 형태)을 만들었다면, 인라인하고 프로토타입에서 왔다고 간략히 적는다. 결정이 풍부한 부분만 남긴다: 작동하는 데모가 아니라 중요한 부분만.