--- name: bf-lead-implement description: Monitor 패턴으로 에픽 내 모든 Story의 구현을 조율한다. 모든 난이도의 Story를 agent에게 위임하고, sprint-status.yaml은 Lead만 갱신한다. --- # Lead Implement (Monitor Pattern) ## Overview 에픽 내 모든 Story의 TDD 구현을 조율하는 Lead 스킬이다. **모든 난이도의 Story를 agent에게 위임**하고 Lead는 코드를 직접 만지지 않는다. sprint-status.yaml은 이 Lead만 갱신한다 (단일 쓰기 지점). ## When to Use - `bf-lead-orchestrate`가 에픽 루프에서 스폰 - 직접 호출하지 않는다. ## Prerequisites - Story 파일: `docs/stories/{TICKET}-story-*.md` (현재 에픽 소속) - `docs/sprint-status.yaml` - `docs/conventions.md` (있으면) - 수정 재실행인 경우: `docs/reviews/{EPIC-ID}-modification.md` (사람의 수정 지시) ## Error Handling - S/M Story agent 스폰 실패: 1회 재시도 후에도 실패 시 해당 Story를 stuck으로 처리 (`status: in_progress` 설정 후 stuck.md에 "agent spawn failed" 기록, `ralph_stuck: true` 설정). Done 신호에서 `"done (stuck: {STORY-ID})"` 목록에 포함하여 orchestrate가 `skipped`로 처리 - L/XL Story Sub-Lead 스폰 실패: 단독 Sonnet agent로 fallback (S/M과 동일 방식) - sprint-status.yaml 쓰기 실패: CLAUDE.md의 Read-yq-Verify Recover 절차를 따른다 (git checkout → 1회 재시도 → 실패 시 orchestrate에 `"error: sprint-status update failed"` 보고) ## Instructions ### 1. 초기 로딩 **yq 전제조건 체크** (최초 1회): ```bash command -v yq >/dev/null 2>&1 || { echo "❌ yq not installed. Install: brew install yq"; exit 1; } ``` - 현재 에픽에 속한 모든 Story 문서를 읽는다. - `docs/conventions.md`를 읽는다. - 수정 재실행인 경우: `modification.md`를 읽어 수정 지시와 대상 Story를 확인한다. - `docs/sprint-status.yaml`을 읽어서 각 Story의 난이도와 상태를 확인한다. - `status: done`인 Story는 건너뛴다. - `status: todo` 또는 `in_progress`인 Story만 대상으로 한다. ### 1b. Convention 섹션 필터링 conventions.md를 읽은 후, 각 Story에 전달할 관련 섹션을 결정한다. **Core 섹션 (항상 포함):** 아키텍처, 네이밍, 테스트, 코드 스타일 **Concern-area 섹션 (Story 파일 경로 기반 필터링):** | Story 파일 경로 패턴 | 포함 섹션 | |---|---| | `src/components/`, `src/pages/`, `src/views/`, `src/layouts/`, `src/hooks/`, `*.tsx`, `*.vue`, `*.svelte`, `app/` (Next.js) | UI 패턴 | | `src/api/`, `src/routes/`, `src/controllers/`, `src/middleware/`, `routes/`, `controllers/`, `server/` | API 패턴 | | `src/models/`, `src/entities/`, `src/repositories/`, `prisma/`, `migrations/`, `src/db/`, `drizzle/` | DB 패턴 | | `src/auth/`, `src/security/`, `src/guards/`, `middleware/auth*` | 보안 패턴 | | `Dockerfile`, `.github/`, `docker-compose*`, `infra/`, `deploy/`, `.env*` | 인프라 패턴 | **필터링 규칙:** 1. conventions.md에 concern-area 섹션이 없으면 전체 내용을 그대로 전달한다 (하위 호환). 2. Story의 Technical Notes에 변경 대상 파일이 없으면 전체 내용을 전달한다 (안전 fallback). 3. 매칭되는 concern-area 섹션이 있으면, Core 섹션 + 매칭 concern-area 섹션만 추출하여 인라인으로 전달한다. 4. 각 `##` 헤딩부터 다음 `##` 헤딩 전까지를 하나의 섹션으로 취급한다. **인라인 전달 형식:** ``` [Project Conventions] 아래는 이 Story에 관련된 프로젝트 컨벤션이다. 전체 컨벤션은 Epic 리뷰 시 Convention Guard가 검사한다. {추출된 섹션 내용} [Library Reference] 아래는 이 Story 구현에 참고할 라이브러리 API 레퍼런스이다. 프로젝트 컨벤션과 충돌 시 컨벤션을 우선한다. ### {라이브러리명} {context7에서 조회한 관련 API 문서 발췌} ### {라이브러리명2} {context7에서 조회한 관련 API 문서 발췌} (Library Reference는 1c에서 조회. 조회 결과가 없으면 [Library Reference] 섹션 전체를 생략한다) ``` ### 1c. Library Reference 조회 conventions.md 필터링 후, 각 Story에 전달할 라이브러리 레퍼런스를 준비한다. **라이브러리 추출:** 1. Story의 Technical Notes에서 "주요 라이브러리" 항목을 확인한다. 2. 라이브러리가 명시되어 있으면, Story당 **최대 3개**까지 context7로 조회한다. 3. 라이브러리가 명시되어 있지 않으면 이 단계를 건너뛴다. **context7 조회 절차 (라이브러리당):** 1. `resolve-library-id`로 library ID를 확인한다. 2. `query-docs`로 Story의 AC에 관련된 API 문서를 조회한다. - query는 Story의 AC를 기반으로 구성한다 (예: "form validation with zod schema", "server actions with next.js app router") - 범용적인 query보다 Story AC에 특화된 query가 효과적이다. 3. 조회 결과에서 **해당 Story AC에 직접 관련된 API/패턴만** 추출한다 (전체 문서 전달 금지). **Fallback:** - `resolve-library-id` 실패 (라이브러리 미발견): 해당 라이브러리는 건너뛴다. - `query-docs` 실패 또는 빈 결과: 해당 라이브러리는 건너뛴다. - context7 MCP 서버 연결 불가: 전체 단계를 건너뛰고 conventions만 전달한다. - 모든 fallback은 정상 동작이다. Library Reference는 보충 자료이지 필수 전제조건이 아니다. ### 2. 모델 (orchestrate가 결정) 이 Lead의 모델은 `bf-lead-orchestrate`가 스폰 시 지정한다: - 에픽 내 L/XL Story 포함 시 Opus - S/M만이면 Sonnet ### 3. Story Agent 스폰 모든 난이도의 Story를 agent에게 위임한다. Lead는 코드를 직접 만지지 않는다. #### S/M Story → 단독 Agent (Sonnet) - Story당 1개의 Sonnet agent를 스폰한다. - `.ralph-progress/{STORY-ID}.json`이 존재하면 초기 retry_count/approaches_count를 해당 파일에서 읽어 전달한다 (이전 중단 복구 시). - Agent에게 전달하는 정보: - Story 문서 내용 (AC, Technical Notes) - conventions 관련 섹션 (1b에서 필터링한 인라인 텍스트) - library reference (1c에서 조회한 인라인 텍스트, 있으면) - 수정 재실행인 경우: modification.md의 해당 Story 수정 지시 원문 - Ralph Loop 지침 (아래 "Story Agent용 Ralph Loop 지침" 참조) - 기존 retry_count/approaches_count (있으면 — 이전 중단에서 복구된 값) - **"sprint-status.yaml을 수정하지 말 것"** - **"`"done"` + commit hash + retry_count + approaches_count 또는 `"stuck"` + stuck.md + retry_count + approaches_count로 보고할 것"** Story agent는 sprint-status.yaml을 절대 읽거나 수정하지 않는다. 모든 상태 업데이트는 Lead가 "done"/"stuck" 신호를 수신한 후 수행한다. "상태만 빨리 기록하겠다"는 단일 쓰기 지점 원칙을 파괴하는 합리화이다. #### L Story → Sub-Lead (Opus) + Implementers (Sonnet) - Story당 1개의 Opus sub-lead를 스폰한다. - Sub-lead에게 전달하는 정보: - Story 문서 내용 - conventions 관련 섹션 (1b에서 필터링한 인라인 텍스트) - library reference (1c에서 조회한 인라인 텍스트, 있으면) - 수정 지시 (있으면) - "Sonnet implementer를 스폰하여 구현을 진행하라" - "쟁점 해소 프로토콜에 따라 조율하라" (아래 참조) - Ralph Loop 지침 - **"sprint-status.yaml을 수정하지 말 것"** - **"`"done"` + commit hash + retry_count + approaches_count 또는 `"stuck"` + stuck.md + retry_count + approaches_count로 보고할 것"** #### XL Story → Sub-Lead (Opus) + 3+ Teammates - Story당 1개의 Opus sub-lead를 스폰한다. - Sub-lead에게 전달하는 정보: - Story 문서 내용 - conventions 관련 섹션 (1b에서 필터링한 인라인 텍스트) - library reference (1c에서 조회한 인라인 텍스트, 있으면) - 수정 지시 (있으면) - "3+ teammates를 스폰하여 구현, 통합, 리뷰 역할을 분담하라" - "discourse로 설계 검증 후 구현을 진행하라" - "쟁점 해소 프로토콜에 따라 조율하라" - Ralph Loop 지침 - **"sprint-status.yaml을 수정하지 말 것"** - **"`"done"` + commit hash + retry_count + approaches_count 또는 `"stuck"` + stuck.md + retry_count + approaches_count로 보고할 것"** ### 4. 병렬 실행 규칙 **디폴트는 병렬이다.** Story 문서의 Technical Notes에서 변경 대상 파일을 확인하고, 아래 순차 조건에 해당하지 않으면 하나의 응답에서 여러 Task tool call을 동시에 보내 병렬 스폰한다. **순차 실행 조건** (이 조건에 해당할 때만 순차): - 파일 겹침: 두 Story의 Target files에 동일 파일이 존재 - Dependencies 명시: Story 문서에 다른 Story에 대한 의존성이 명시됨 - 공유 파일 충돌: 아래 파일을 변경하는 Story는 "겹침"으로 간주 - Lock 파일: `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `Gemfile.lock`, `poetry.lock`, `go.sum` - 공유 설정: `tsconfig.json`, `webpack.config.*`, `vite.config.*`, `.env.*` - 자동 생성 파일: `schema.prisma` → `@prisma/client`, DB migration 파일 - Story agent가 lock 파일을 변경해야 하는 경우 (새 의존성 설치 등): 해당 Story를 병렬 그룹에서 분리하여 단독 또는 순차 실행 ### 5. 모니터링 루프 sprint-status.yaml 갱신은 CLAUDE.md의 **Read-yq-Verify** 프로토콜을 따른다. 각 Story agent로부터 완료 신호를 수신한다: **"done" + commit hash 수신 시:** - sprint-status.yaml 업데이트 (Lead가 직접, `yq -i` 명령어 사용): ```bash yq -i ' ....status = "done" | ....tdd = "done" | ....model_used = "sonnet" | ....ralph_retries = 1 | ....ralph_approaches = 0 | ....ralph_stuck = false ' docs/sprint-status.yaml ``` - `model_used`: 실제 사용된 모델 전략 (`"sonnet"` / `"opus-lead"` / `"opus-lead+3"`) - `ralph_retries`: agent가 보고한 재시도 횟수 - `ralph_approaches`: agent가 보고한 접근 전환 횟수 **"stuck" + stuck.md 수신 시:** - sprint-status.yaml 업데이트: ```bash yq -i ' ....ralph_stuck = true | ....ralph_retries = 3 | ....ralph_approaches = 2 ' docs/sprint-status.yaml ``` - stuck.md를 `docs/reviews/{STORY-ID}-stuck.md`에 저장한다. - **git commit하지 않는다** — docs/ 산출물은 Phase 4 Archive에서 일괄 커밋한다. - **다른 Story들은 계속 진행한다** (stuck Story가 있어도 나머지를 중단하지 않음). - **stuck Story의 `status`는 변경하지 않는다** — orchestrate가 자동 판단 시 `skipped`로 변경한다. 모든 Story가 완료(done 또는 stuck)될 때까지 대기한다. ### 6. Done 신호 모든 Story 완료 후 orchestrate에 보고한다: **stuck Story 없음:** - `"done"` + sprint-status.yaml 경로 **stuck Story 있음:** - `"done (stuck: {STORY-ID}, {STORY-ID})"` + sprint-status.yaml 경로 + stuck.md 경로들 종료 (컨텍스트 소멸). --- ## Story Agent용 Ralph Loop 지침 Story agent에게 전달할 TDD 지침이다. Agent는 이 지침을 그대로 따른다. ### TDD 사이클 구현 코드를 작성하기 전에 반드시 테스트를 먼저 작성하고 Red(실패)를 확인해야 한다. 테스트와 구현을 동시에 작성하는 것은 TDD 위반이다. "간단하니까 같이 작성해도 된다"는 이 게이트를 우회하는 전형적인 합리화이다. 1. **단위 테스트 작성** (AC 기반) 2. **Red 확인**: 테스트 실행 → 실패 확인 - 예상대로 실패하지 않으면 테스트 코드 수정 3. **구현** 4. **Green 확인**: 테스트 재실행 → 통과 확인 - 실패하면 아래 가드레일에 따라 재시도 5. **리팩토링** (필요 시, Green 유지 확인) 6. **git commit**: `[{TICKET}] {간단한 설명}` - **`docs/` 하위 파일은 커밋에 포함하지 않는다** (sprint-status.yaml, story 문서 등 모두 제외) 7. **Lead에 done 보고**: `"done"` + commit hash + `retry_count` + `approaches_count` ### Ralph Loop 가드레일 **a) 최대 재시도 횟수: 5회** - Green 검증 실패 → 구현 수정 → 재실행을 최대 5회까지 반복한다. - `retry_count`를 0부터 시작하여 매 실패마다 1 증가. **b) 반복 실패 감지 (Stuck Detection)** - 매 실패 시 에러 메시지의 핵심 내용(에러 타입 + 발생 파일)을 기록한다. - 직전 시도와 동일한 근본 원인의 에러가 연속 2회 발생하면, 접근 방식을 전환한다: 1. 테스트 코드의 기대값이 잘못되었는지 재검토 2. AC 해석이 올바른지 Story 파일 재확인 3. 구현 전략을 근본적으로 변경 (다른 알고리즘/패턴 적용) - `approaches_count`를 0부터 시작하여 전환 시마다 1 증가. **c) 진행 상태 파일 기록 (크래시 복구용)** - 매 재시도 후 `.ralph-progress/{STORY-ID}.json`에 현재 상태를 기록한다: ```json { "retry_count": 2, "approaches_count": 1, "last_error_type": "TypeError", "last_error_file": "src/auth/login.ts" } ``` - 이 파일은 에이전트 크래시 후 bf-resume이 재개할 때 Ralph Loop 카운트를 복원하는 데 사용된다. - Story 완료("done") 또는 stuck 보고 후 해당 파일을 삭제한다. - **sprint-status.yaml은 여전히 수정하지 않는다** — 이 파일은 ralph-progress 전용이다. **d) 한도 초과 시 → "stuck" 보고** - `retry_count >= 5`에 도달하면 루프를 즉시 중단한다. - **stuck.md를 작성한다:** ```markdown # Stuck 보고서: {STORY-ID} ## 재시도 횟수: {retry_count}/5 ## 접근 전환 횟수: {approaches_count} ## 시도한 접근 방식 1. {접근 1} — {에러 요약} 2. {접근 2} — {에러 요약} ## 마지막 에러 {전체 에러 출력} ## 변경된 파일 {변경 파일 목록} ``` - `.ralph-progress/{STORY-ID}.json` 삭제 - Lead에 보고: `"stuck"` + stuck.md 경로 - `retry_count`, `approaches_count`를 함께 보고한다. ### 쟁점 해소 프로토콜 (L/XL Story의 Sub-Lead/Teammates에 적용) 1. **Teammates 직접 대화**: `SendMessage`로 직접 challenge/agree/보완 - 합의됨 → Sub-Lead에 결론만 보고 2. **미합의 시 Sub-Lead 중재**: 프로젝트 방향성(conventions.md) 기준으로 판단 - Sub-Lead 결정으로 확정 3. **그래도 미합의 → 버린다 (기록)**: 더 이상 토큰을 쓰지 않음 ### Agent Teams 실패 시 Fallback (L/XL) - Task tool 에러 (agent 생성 실패): 단독 Sonnet agent로 fallback (S/M과 동일 방식) - Teammate 10분 이상 무응답: 해당 teammate 제외하고 나머지로 진행 - 프로세스 비정상 종료: 단독 Sonnet agent로 fallback ## Output Format - sprint-status.yaml 업데이트 (이 Lead가 구현 단계의 유일한 쓰기 지점) - `"done"` 또는 `"done (stuck)"` 신호를 orchestrate에 전달