| name | jira-task-loop |
| description | Drain the initialized Jira task queue — auto + local merge per task, with base rebase between tasks. Triggers: jira-task loop; 루프 실행, 큐 소진. |
| user-invocable | false |
| argument-hint | [--skip <단계,...>] |
| allowed-tools | ["Read","Edit","Bash","Skill","EnterWorktree","ExitWorktree"] |
jira-task-loop: Drain Task Queue
Language Rule: 프로젝트 CLAUDE.md의 Conventions 섹션 참고 (한국어 출력, Jira 코멘트 제목은 영어).
Overview
init된 태스크 큐를 순차 소진하는 태스크 레벨 오케스트레이터. auto가 한 태스크 안의 단계 루프라면, loop는 태스크 사이의 루프다.
- 태스크마다:
auto(start→approach→impl+test→review) → review 게이트 확인 → jira-local-merge(In Review 전이)
- 머지 직후, 큐에 남은 worktree 브랜치를 최신 base로 rebase (stale base 방지).
done / pr / clean은 포함하지 않음 — "merge 후 main에서 사람이 확인"하는 게이트를 보존. 루프가 끝나면 모든 처리 태스크는 Jira "In Review" 상태로 정렬된다.
- 태스크-로컬 실패는 격리(quarantine)하고 계속, 시스템 실패만 전체 중단. auto 게이트 미통과·scope shortfall·단계 실패·merge 충돌·rebase 충돌은 전부 그 태스크만
deferred로 보류하고 다음 태스크로 간다 (Step 2.5). 연속 동일 단계 실패 2건이나 인프라 시그니처(인증/MCP/base 손상)만 전체 중단 (Step 4). 사람의 역할은 루프 끝의 예외 리포트 검토다 (Step 3). 설계: tasks/loop-quarantine-design.md.
- 재진입 가능: 큐는 매번
completedSteps에서 재계산 — 중단·격리 후 /jira-task loop 재실행하면 격리 태스크를 포함해 이어서 진행.
핵심 원칙: 오케스트레이터는 가볍게 유지한다. context 읽기, Skill 호출, rebase, 판정만 수행. 단계별 작업·산출물은 모두 auto/merge 스킬(과 그 sub-agent)에 위임하고, 산출물 본문을 컨텍스트로 가져오지 않는다.
Step 1: Load Queue
-
cwd의 .jira-context.json을 Read. tasks[] 배열이 있으면 aggregate. 없으면(worktree-local) 그 안의 repoRoot로 <repoRoot>/.jira-context.json을 다시 Read. 이 시점에 REPO_ROOT와 aggregate 절대 경로를 확정한다 — 루프 중 cwd가 각 worktree로 이동하므로, 이후 모든 aggregate Read/Edit은 반드시 이 절대 경로를 사용.
-
aggregate가 없거나 tasks[]가 비어 있으면 중단:
❌ Loop 실행 불가: init된 태스크 큐가 없습니다.
먼저 /jira-task init [count|ISSUE-KEY]로 작업 환경을 세팅하세요.
-
deferred 초기화: tasks[]에 deferred / deferredKind / deferredReason이 남아 있으면 셋 다 제거(Edit, JSON 유효성 유지) — 새 run은 보류 태스크를 재시도한다 (사유가 여전하면 다시 보류될 뿐).
-
큐 계산: tasks[] 중 completedSteps(없으면 빈 배열로 간주)에 "merge"가 없고 status가 Done/완료가 아닌 태스크. 배열 순서 유지 (init이 priority 순으로 기록).
-
인자에 --skip <단계,...>가 있으면 보관해 두고 모든 auto 호출에 그대로 전달한다 (단계 검증은 auto가 수행). 단, review가 포함되어 있으면 목록에서 제거하고 경고: review 완료가 merge 게이트 조건이므로 loop에서는 review 스킵 불가.
⚠ loop에서는 review를 스킵할 수 없습니다 (merge 게이트 조건) — review는 실행됩니다.
큐가 비어 있으면:
✅ 큐가 비어 있습니다 — 모든 태스크가 이미 merge 완료.
main에서 동작 확인 후 /jira-task done <TASK-ID>로 마무리하세요.
큐가 있으면 실행 계획을 보여주고 바로 시작:
🔁 Loop 모드: <N>개 태스크 처리 예정
큐: <TASK-ID-1> → <TASK-ID-2> → ...
태스크당: auto(start→review) → local merge(In Review) | done은 수동 (main 확인 후)
Step 2: Per-Task Loop
큐의 각 태스크에 대해 순서대로 (한 번에 한 태스크, 병렬 금지 — merge 순서 의존성):
2-a. worktree로 이동 + auto 실행
먼저 세션 cwd를 해당 태스크의 worktree로 옮긴다 — Bash cd가 아니라 EnterWorktree({ path: "<worktreePath>" }):
⛔ Bash cd는 셸에만 적용된다. auto launcher의 Read와 Workflow가 띄우는 stage agent는 세션 cwd를 보므로, cd만 하면 launcher가 main의 aggregate를 읽고 cwd 불일치로 중단한다(또는 impl이 main 작업 트리를 수정한다). EnterWorktree만이 세션 cwd를 바꾼다.
EnterWorktree 세션은 격리 모드라 git -C <repoRoot> 같은 외부 경로 git 명령과 복잡한 bash를 거부한다. 이 안에서는 auto 호출과 단순 명령만 하고, merge 전에 반드시 ExitWorktree({ action: "keep" })로 main에 복귀한다 (2-b).
aggregate(절대 경로)를 다시 Read해 해당 태스크의 completedSteps에 "review"가 이미 있으면 auto를 건너뛰고 2-b로.
아니면: Skill({ skill: "jira-integration:jira-task-auto", args: "<TASK-ID>[ --skip <단계,...>]" })
완료 후 aggregate를 다시 Read해 "review"가 completedSteps에 들어왔는지 확인.
-
없으면 → auto가 중단된 것. 종류 판정: worktree의 docs/run-log/_index.jsonl 마지막 줄(해당 taskId)의 status/failedStage/reason을 읽는다 (auto launcher Step 5.5가 기록). run-log가 없으면 auto가 출력한 중단 메시지의 status 키워드로 판정.
| auto status | deferredKind |
|---|
scope_shortfall | scope-shortfall |
fix_exhausted / fix_unconverged | gate-exhausted |
aborted (단계 실패) | stage-failed |
→ Step 4의 시스템 실패 판정을 먼저 거친다. 시스템 실패가 아니면 Step 2.5로 격리하고 다음 태스크로.
2-b. local merge
먼저 ExitWorktree({ action: "keep" })로 main 세션 cwd로 복귀한다 (격리 해제 — merge는 git -C <repoRoot>가 필요). 이미 main이면 no-op.
Skill({ skill: "jira-integration:jira-local-merge", args: "<TASK-ID>" }) — merge 스킬의 worktree 측 작업(pre-flight, 스마트 커밋)은 git -C "<worktreePath>"로 수행한다.
완료 후 aggregate를 다시 Read해 "merge"가 completedSteps에 들어왔는지 확인. 없으면 → merge 실패. base 브랜치 원상 복구가 필수다 (더러워진 base는 이후 모든 태스크를 오염시킨다):
git -C "<REPO_ROOT>" merge --abort 2>/dev/null || true
git -C "<REPO_ROOT>" status --porcelain
base가 깨끗하면 deferredKind: "merge-failed"로 Step 2.5 격리 후 다음 태스크로.
2.5. 격리 (quarantine)
EnterWorktree 상태면 먼저 ExitWorktree({ action: "keep" })로 main에 복귀한다 (다음 태스크의 EnterWorktree는 main에서만 가능). 그다음 aggregate의 해당 태스크 항목에 Edit으로 기록하고 이번 run의 큐에서 제외한다:
"deferred": true,
"deferredKind": "<scope-shortfall | gate-exhausted | stage-failed | merge-failed | rebase-conflict>",
"deferredReason": "<한 줄 — auto/merge/git 출력 기반 실제 사유>"
- worktree의 작업 트리는 건드리지 않는다 (사람이 볼 증거).
- Jira는 손대지 않는다 (상태 전이·코멘트 없음 — 격리는 로컬 워크플로 상태).
- 격리된 태스크는 2-c rebase 대상에서도 제외.
- 같은 run 안에서 재시도하지 않는다 (사유가 그대로면 무한 루프). 재시도 단위는 다음 run.
출력:
⛔ <TASK-ID> 격리 [<deferredKind>]: <사유 한 줄> — 다음 태스크로 계속합니다.
2-c. 잔여 worktree rebase
큐에서 아직 처리하지 않은 (deferred 아닌) 각 태스크에 대해:
git -C "<worktreePath>" rebase <baseBranch>
실패 시:
git -C "<worktreePath>" rebase --abort 2>/dev/null || true
(rebase가 시작조차 못 한 경우 — dirty worktree 등 — abort할 대상이 없으므로 에러는 무시한다.)
→ Step 2.5 격리 (deferredKind: "rebase-conflict"). 사유는 git 출력에서 판별해 정확히 기록한다 — 예: "rebase conflict after <방금 merge한 TASK-ID>" / "dirty worktree — rebase 시작 불가".
아직 시작 전인 브랜치(커밋 없음)는 rebase가 fast-forward로 끝난다 — 항상 시도한다.
2-d. 진행 메시지
✅ <TASK-ID> merged (<i>/<N>) → 다음: <NEXT-TASK-ID>
Step 3: 예외 리포트 (Completion Summary)
큐 소진 시, EnterWorktree 상태면 ExitWorktree({ action: "keep" })로 main에 복귀한 뒤 사람의 결정 목록 형태로 보고한다. 클린 통과 줄의 수치는 merge 스킬이 Jira 코멘트에 쓴 commit/file/line 카운트를 재사용:
─────────────────────────────────────────
🔁 Loop 완료 — 통과 <N> / 격리 <M> / 미착수 <K>
─────────────────────────────────────────
✅ 클린 통과 (In Review, 사람 확인만 필요):
<TASK-ID> +<add>/-<del>, <n> files — <merge 코멘트의 한 줄 요약>
⛔ 격리 — 결정 필요:
<TASK-ID> [<deferredKind>] <사유> — 권장: <kind별 한 줄>
─────────────────────────────────────────
격리 태스크는 사유 해결 후 /jira-task loop 재실행 시 자동 재시도됩니다.
클린 통과는 main에서 동작 확인 후 /jira-task done <TASK-ID>.
kind별 권장 한 줄 (고정 템플릿):
| deferredKind | 권장 |
|---|
| scope-shortfall | 부분 수용 merge 후 미구현분 별도 init, 또는 worktree에서 추가 구현 후 review 재실행 |
| gate-exhausted | docs/review/<TASK-ID>.review.md 확인 → worktree에서 수동 수정 → test → review |
| stage-failed | 실패 단계부터 직접 실행: /jira-task <단계> <TASK-ID> |
| merge-failed | worktree에서 base 충돌 해결 후 loop 재실행 |
| rebase-conflict | worktree에서 rebase 충돌 해결(또는 미커밋 정리) 후 loop 재실행 |
격리가 0건이면 "⛔ 격리" 블록을 생략한다.
3.5. run-log 기록 (관측용, non-blocking)
script-lookup.md로 append-run-log.py를 해석한 뒤, REPO_ROOT에서 Bash 1회:
printf '%s' '{"status":"loop-run","passed":[<통과 TASK-ID>],"quarantined":[{"taskId":"<ID>","deferredKind":"<kind>"}]}' \
| python3 "$APPEND_RUN_LOG_PY" - - - "docs/run-log" --kind=loop-run
Step 4: 시스템 실패 판정 + Abort Report
태스크-로컬로 위장한 시스템 문제(깨진 base, Jira 인증 만료, MCP 다운)는 격리-계속하면 큐 전체가 무의미하게 소진된다. 다음 중 하나면 전체 중단, 아니면 Step 2.5 격리:
- 연속 동일-단계 실패 2건 — 서로 다른 태스크가 같은
failedStage(또는 같은 deferredKind가 merge-failed)로 연속 실패.
- 인프라 시그니처 — auto/merge 실패 사유에 다음이 포함: HTTP 401/403·
Unauthorized·authentication, MCP 연결 실패(mcp+connect/not available), base 브랜치 checkout/상태 복구 실패. 목록은 보수적으로 짧게 유지 — 과잉 매칭이 격리의 가치를 죽인다.
전체 중단 시 EnterWorktree 상태면 ExitWorktree({ action: "keep" })로 main에 복귀한 뒤 보고:
❌ Loop 중단 (시스템 실패 의심): <판정 근거 — 연속 <단계> 실패 2건 | 인프라 시그니처 "<매칭 문자열>">
마지막 실패: <TASK-ID> — <사유>
처리 완료: <이번 run에서 merge까지 끝난 TASK-ID 목록 또는 "없음">
격리: <TASK-ID [kind] 목록 또는 "없음">
남은 큐: <미처리 TASK-ID 목록>
원인을 해결한 뒤 /jira-task loop를 재실행하세요 — 완료된 태스크는 건너뛰고 격리 태스크는 재시도됩니다.