| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| Invoke | /dev:batch |
| Category | kobic-workflow |
| MCP Servers | zenhub, sequential |
/dev:batch โ Epic ํ ๋ฐฉ์ ์๋ ์ฒ๋ฆฌ#
ํ๋ง๋๋ก#
Epic(ํฐ ์์ ๋ฌถ์) ํ๋๋ฅผ ๋์ ธ์ฃผ๋ฉด, ๊ทธ ์๋์ ๋งค๋ฌ๋ฆฐ StoryยทSub-task๋ค์ ์์๋๋ก ํ๋์ฉ ์๋์ผ๋ก ๊ฐ๋ฐยท๊ฒํ ยท๋ณํฉํด ์ฃผ๋ ์๋ํ ๋ช ๋ น์ ๋๋ค. ํฐ ํ๋ก์ ํธ๋ฅผ ์์ ์กฐ๊ฐ์ผ๋ก ๋๋ ํ ์กฐ๊ฐ์ฉ ๋๋ด๊ณ , ๋ง์ง๋ง์ ์ ์ฒด๋ฅผ ํ ๋ฉ์ด๋ฆฌ๋ก ํฉ์ณ ๋ณด๊ณ ํ๋ "์กฐ๋ฆฝ ๋ผ์ธ"์ด๋ผ๊ณ ๋ณด๋ฉด ๋ฉ๋๋ค.
๋๊ฐยท์ธ์ ์ฐ๋์#
- ์ฌ๋ฌ ๊ฐ์ ํ์ ์์ (Story, Sub-task)์ด ๋ธ๋ฆฐ Epic ํ๋๋ฅผ ํต์งธ๋ก ์งํํ๊ณ ์ถ์ ๋
- ์์ ํ๋ํ๋ ์ง์ ๋ธ๋์น๋ฅผ ๋ง๋ค๊ณ PR์ ์ฌ๋ฆฌ๊ณ ๋ณํฉํ๋ ๋ฐ๋ณต ์์ ์ ์ฌ๋์ด ์ผ์ผ์ด ์ฑ๊ธฐ์ง ์๊ณ ์๋์ผ๋ก ๋๋ฆฌ๊ณ ์ถ์ ๋
- ๋ง์ง๋ง์ ์ฌ๋์ด ํ ๋ฒ๋ง ์ต์ข ์น์ธํ๋ฉด ๋๋, ์์ด ๋ ๊ฐ๋ ์ผ๊ด ์ฒ๋ฆฌ ๋ฐฉ์์ ์ํ ๋
๋ฌด์์ ํด์ฃผ๋์#
- Epic ์ ์ฉ ์์
๊ณต๊ฐ(
epic/{๋ฒํธ}-{์ด๋ฆ}๋ธ๋์น)์ ๋จผ์ ๋ง๋ค๊ณ , ๊ทธ ์์์ Story๋ค์ ์ฐจ๋ก๋ก ์ฒ๋ฆฌํฉ๋๋ค. - ๊ฐ StoryยทSub-task๋ง๋ค ๊ฐ๋ฐ โ ํ ์คํธ โ ๊ฒํ (Code Review) โ PR ์์ฑ โ ๋ณํฉ์ ์๋์ผ๋ก ์งํํฉ๋๋ค.
- ๋ณํฉ์ด ๋๋๋ฉด ํด๋น ZenHub ์ด์๋ฅผ ์๋์ผ๋ก "์๋ฃ(Close)" ์ฒ๋ฆฌํ๊ณ , ์ ๋๋ก ๋ซํ๋์ง๊น์ง ๋ค์ ํ์ธ(๊ฒ์ฆ)ํฉ๋๋ค.
-
๋ชจ๋ Story๊ฐ ๋๋๋ฉด Epic ์ ์ฒด๋ฅผ ๋ฌถ์ ์ต์ข
PR์ ๋ง๋ค์ด
development(๊ฐ๋ฐ ํตํฉ ๋ธ๋์น)๋ก ํฉ์น ์ค๋น๋ฅผ ๋ง์นฉ๋๋ค. ์ด ๋ง์ง๋ง ๋ณํฉ๋ง ์ฌ๋์ ์น์ธ์ ๊ฑฐ์นฉ๋๋ค.
์ด๋ป๊ฒ ์ฐ๋์#
/dev:batch {epic_number}
# ์: 1234๋ฒ Epic์ ํต์งธ๋ก ์ฒ๋ฆฌ
/dev:batch 1234
{epic_number}์๋ฆฌ์๋ ์ฒ๋ฆฌํ Epic์ ์ด์ ๋ฒํธ๋ฅผ ๋ฃ์ต๋๋ค.- ๊ทธ ๋ค๋ก๋ ๋ช ๋ น์ด ์์์ Epic ์๋์ StoryยทSub-task๋ฅผ ์ฐพ์ ์์๋๋ก ์ฒ๋ฆฌํ๋ฏ๋ก, ์ถ๊ฐ๋ก ์ ๋ ฅํ ๊ฒ์ ์์ต๋๋ค.
์์์ ๋ฌด์จ ์ผ์ด ๋ฒ์ด์ง๋์#
์ ์ฒด ํ๋ฆ์ ํฌ๊ฒ ์ธ ๋จ๊ณ์ ๋๋ค.
-
(์ ํ) ๊ธฐํ ๋ช
์ธ ์ ๋ ฌ ์ ๊ฒ โ
docs/seed-spec-*.md(ํ์ /LOCKED) ๊ฐ ์์ผ๋ฉด, ๋ฐฐ์น๋ฅผ ์์ํ๊ธฐ ์ ๊ณผ ๋ง์ง๋ง ๋ณํฉ ์งํ์ "์ง๊ธ ์ฒ๋ฆฌํ๋ ค๋(๋๋ ๋๋ธ) ์์ ๋ฌถ์์ด ์๋ ๊ธฐํ ๋ชฉํยทํฉ๊ฒฉ ๊ธฐ์ค๊ณผ ๊ฐ์ ๋ฐฉํฅ์ธ๊ฐ"๋ฅผ ์๋ฏธ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ฒ์ฉ ๋์กฐํฉ๋๋ค(์ฝ๊ธฐ ์ ์ฉ,/spec:status์ฌ์ฌ์ฉ). ๋ฐฉํฅ์ด ํฌ๊ฒ ์ด๊ธ๋๋ฉด(MAJOR-DRIFT) ๊ฒฝ๊ณ ๋ฅผ ๋์ฐ๊ณ "๊ทธ๋๋ ๊ณ์ํ ๊น์?"๋ฅผ ๋ฌผ์ด ๋ฉ์ถฅ๋๋ค. ํ์ ๋ ๊ธฐํ ๋ช ์ธ๊ฐ ์์ผ๋ฉด ์ด ์ ๊ฒ์ ์กฐ์ฉํ ๊ฑด๋๋๋๋ค(์๋ธ๋ชจ๋ ์์ ๋์ฒ๋ผ ์๋ฌด ์ผ๋ ์ ํจ โ ๊ธฐํ ์์ด๋ ์ ์ ์งํ). -
Epic ์์
๊ณต๊ฐ ๋ง๋ค๊ธฐ (Phase 1) โ Epic ์ ๋ณด๋ฅผ ์กฐํํ๊ณ , ์ต์ ์ํ์
development์์ Epic ์ ์ฉ ๋ธ๋์น๋ฅผ ์๋ก ๋ง๋ญ๋๋ค. Epic์ "์งํ ์ค(In Progress)"์ผ๋ก ํ์ํฉ๋๋ค. - Story ํ๋์ฉ ์ฒ๋ฆฌํ๊ธฐ (Phase 2) โ Story๋ง๋ค ๊ฐ๋ฐยทํ ์คํธยท๊ฒํ ยทPRยท๋ณํฉ์ ์ฐจ๋ก๋ก ๋๋ฆฝ๋๋ค. Story ์๋์ Sub-task๊ฐ ๋ ์์ผ๋ฉด ๊ทธ๊ฒ๋ค๋ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋จผ์ ์ฒ๋ฆฌํฉ๋๋ค. ๊ฐ ๋ณํฉ ํ์๋ ์ด์๋ฅผ ์๋์ผ๋ก ๋ซ๊ณ , ๋ซํ๋์ง ๊ฒ์ฆํ ๋ค, ์์ ๊ธฐ์ค ๋ธ๋์น๋ฅผ ์ต์ ์ํ๋ก ๋ง์ถฅ๋๋ค.
-
Epic ๋ง๋ฌด๋ฆฌ (Phase 3) โ ๋ชจ๋ Story๊ฐ ํฉ์ณ์ก๋์ง ํ์ธํ๊ณ , Epic ์ ์ฒด๋ฅผ ๋ฌถ์ ์ต์ข
PR์
development๋ก ๋ง๋ญ๋๋ค. ์ฌ๋์ ์ต์ข ์น์ธ์ ๋ฐ์ ๋ค ๋ณํฉํ๋ฉด Epic ์ด์๊ฐ ์๋์ผ๋ก ์ข ๋ฃ๋ฉ๋๋ค.
โ๏ธ ์์ธ ์ต์ ยท์คํ ๋ช ์ธ (๊ฐ๋ฐ์ / AI ์์ด์ ํธ์ฉ)
Usage#
/dev:batch {epic_number}Hierarchical Branch Strategy#
development
โโโ epic/{epic_number}-{slug} โ Created in Phase 1
โโโ story/{story_1}-{slug} โ Sequentially processed in Phase 2
โ โโโ task/{subtask_1}-{slug} โ Sub-tasks processed sequentially within Story
โ โโโ task/{subtask_2}-{slug}
โโโ story/{story_2}-{slug}
โโโ story/{story_3}-{slug}
โ Epic โ development PR in Phase 3Workflow#
Phase 0: Tooling Preflight & Recoverability (GD-02)#
Epic ๋ฃจํ๋ฅผ ์์ํ๊ธฐ ์ ์ ํ ๋ฒ ํ์ ๋๊ตฌ๋ฅผ ์ ๊ฒํ๊ณ , ์ค๋จ ์ ์ฌ๊ฐ ๋ฐฉ๋ฒ์ ๋ช ์ํ๋ค.
1. Tooling preflight (loop ์ง์
์ 1ํ):
- command -v gh & & gh auth status โ ๋ฏธ์ธ์ฆ์ด๋ฉด ์ฆ์ ์ค๋จ(๋จธ์ง ์ ์ ์คํจ, Epic ์ค๊ฐ ์๋)
- command -v melos / dart / flutter โ ๋ถ์ฌ ์ /dev:run " Step 0 Degradation Contract " ๋ฅผ ๋ฐ๋ฅธ๋ค(์ฐํ + ๊ฒฝ๊ณ )
2. Resume ๊ณ์ฝ (๋ฉฑ๋ฑ):
- ์ด๋ฏธ Closed ์ธ Story ๋ ๊ฑด๋๋ด๋ค(Phase 2์์ ์ํ ํ์ธ).
- ์ค๊ฐ ์คํจ ์ `/dev:batch {epic}` ๋ฅผ ๋ค์ ์คํํ๋ฉด ๋ง์ง๋ง์ผ๋ก ๋จธ์ง๋ Story ๋ค์๋ถํฐ ์ด์ด์ ์ฒ๋ฆฌํ๋ค.
- ๋ฐ๋ผ์ ํ Story ์คํจ๊ฐ Epic ์ ์ฒด๋ฅผ " ์ ๋ฐ-๋จธ์ง " ๋ก ์๊ตฌ ๊ณ ์ฐฉ์ํค์ง ์๋๋ค.๋ฐฐ๊ฒฝ: ๊ณผ๊ฑฐ batch ๋ /dev:run ์ ํ๋ํ์ผ์ ๊ทธ๋๋ก ์์ํ๊ณ post-merge git/gh/submodule ํธ์ถ๋ ๊ฐ๋๊ฐ ์์ด, ๋๊ตฌ 1๊ฐ ๋ถ์ฌ๋ ํ Story ์คํจ๊ฐ Epic ์ ์ค๊ฐ์ ์ข์ด์์ผฐ๋ค(GD-02). ์๋ Phase 2 ์ post-merge ๋๊ธฐํ ์คํจ๋ ๊ฒฝ๊ณ ํ ๊ณ์ํ๋ค(์ค๋จ ์๋).
Phase 0.5: Optional Seed-Alignment Checkpoint (D4 โ opt-in, no-op on absence)#
Epic ๋ฐฐ์น๋ ๊ธธ๊ฒ ๋๋ ์๋ํ๋ผ, ์์
๋ฌถ์์ด ์๋ ๊ธฐํ์์ ์กฐ๊ธ์ฉ ํ๋ฅ(drift) ํ๊ธฐ ์ฝ๋ค. ๊ทธ๋์ docs/seed-spec-*.md ๊ฐ ์กด์ฌํ๊ณ Status: LOCKED ์ผ ๋์ ํํด, ๋ฐฐ์น ์์ ์ง์ (pre-batch) ๊ณผ Phase 3 ์ต์ข
๋ณํฉ ์งํ(post-batch) ์ ์ ๋ ฌ ์ ๊ฒ์ ํ ๋ฒ์ฉ ์คํํ๋ค. ์ด ์ ๊ฒ์ /spec:status ํ๋กํ ์ฝ์ ๊ทธ๋๋ก ์ฌ์ฌ์ฉํ๋ค โ ์ฝ๊ธฐ ์ ์ฉ(์๋ฌด ํ์ผ๋ ์ฐ์ง ์์), ์ต์ LOCKED docs/seed-spec-*.md ๋ก๋, ์ ํ์ --against {file|dir|PR}.
0.5-1. Seed ํ์ง (๊ฒ์ดํธ):
- SEED=$(ls -1 docs/seed-spec-*.md 2 > /dev/null | tail -1)
- SEED ๊ฐ ์์ผ๋ฉด โ **no-op (์กฐ์ฉํ skip)**. ๊ฒฝ๊ณ ๋ ์ฐจ๋จ๋ ์์.
- SEED ๊ฐ ์์ผ๋ ๋จธ๋ฆฌ๊ธ์ด `Status: LOCKED` ๊ฐ ์๋๋ฉด(DRAFT) โ **no-op + 1์ค ์๋ด**( " ๊ธฐํ ๋ช
์ธ ๋ฏธํ์ โ ์ ๋ ฌ ์ ๊ฒ skip " ). DRAFT ๋ฅผ ๊ฐ์ ๋ก ์ ๊ทธ์ง ์๋๋ค.
- โ ์ฆ, ์๋๊ฐ ์๊ฑฐ๋ ๋ฏธํ์ ์ธ ์ ์ฅ์์์ `/dev:batch` ๋ **์ ๋ ํ๋ํ์ผํ์ง ์๋๋ค**(submodule ๋๊ธฐํ no-op ์ ๋์ผํ graceful degradation ํํ).
0.5-2. ์ ๋ ฌ ์ ๊ฒ (LLM-SEMANTIC, /spec:status ์ฌ์ฌ์ฉ):
- ๋์กฐ ๋์:
- pre-batch โ ์ด๋ฒ Epic ์๋ **๊ณํ๋** StoryยทSub-task ์ด์ ์งํฉ(์ ๋ชฉ/๋ณธ๋ฌธ/AC)
- post-batch โ Phase 3 ๋ณํฉ ์งํ **์๋ฃ(Closed)๋** ์ด์ ์งํฉ + Epic ๋ธ๋์น ๋์ ๋ณ๊ฒฝ(diff/PR)
- `/spec:status --against {๊ณํ/์๋ฃ ์ด์ ์งํฉ ๋๋ Epic PR}` ๋ฅผ ์คํํด LLM-SEMANTIC ์ ๋ ฌ ๋ฆฌํฌํธ๋ฅผ ๋ฐ๋๋ค:
- **AC-ID ์ํ echo** โ Seed S4(Acceptance Boundaries)์ ๊ฐ ํฉ๊ฒฉ ๊ธฐ์ค์ `AC-01`, `AC-02` โฆ ๋ก ์๋ณํ๊ณ PENDING / IN-PROGRESS / MET / FAILED / WAIVED ๋ก ํ๊ธฐ.
- **๋ชฉํ ์ ๋ ฌ ํ์ ** โ ์์
๋ฌถ์์ ์๋๊ฐ Seed S1(Core Problem)๊ณผ ๊ฐ์ ๋ฐฉํฅ์ธ์ง ALIGNED / MINOR-DRIFT / MAJOR-DRIFT ๋ก ํ์ .
- **์ ์ฝ ์๋ฐ ํ๋๊ทธ** โ ์์
์ด ์๋ฐํ๋ ๊ฒ์ผ๋ก ๋ณด์ด๋ Seed S2(Immutable Constraints) ํญ๋ชฉ์ ๋์ด.
- โ ๏ธ **์๋ฏธ ๊ธฐ๋ฐ ํ๋จ๋ง ์ฌ์ฉํ๋ค(์ ๋ ์ดํ/ํ ํฐ ์ผ์น ์๋)**: Seed ์ ์ด์/AC ํ
์คํธ๋ **ํ๊ตญ์ด ์ฐ์ **์ด๋ผ ๊ณต๋ฐฑยทํ ํฐ ๊ฒน์นจ ๋งค์นญ์ ์กฐ์ฌยท๋์ด์ฐ๊ธฐ์์ ๊นจ์ง๋ค. drift/์ ๋ ฌ/AC ๋งค์นญ์ ๋ชจ๋ **LLM ์๋ฏธ ํ๋จ**์ผ๋ก ์ํํ๋ค(`/spec:status` ์ ๋์ผ ์์น).
0.5-3. ํ์ ๋ณ ํ๋:
- ALIGNED / MINOR-DRIFT โ ๋ฆฌํฌํธ๋ง ์ถ๋ ฅํ๊ณ ๊ทธ๋๋ก ์งํ(์ฐจ๋จ ์๋).
- **MAJOR-DRIFT โ ๊ฒฝ๊ณ ํ ์ผ์์ ์ง(ํ์)**: ์ด๊ธ๋ ์ง์ ์ ์์ฝํด ๋ณด์ฌ์ฃผ๊ณ `AskUserQuestion` ์ผ๋ก " ๊ธฐํ๊ณผ ํฌ๊ฒ ์ด๊ธ๋จ โ ๊ทธ๋๋ ๊ณ์ํ ๊น์? " ํ์ธ์ ๋ฐ๋๋ค.
- pre-batch: ์ฌ์ฉ์๊ฐ " ์ค๋จ " ์ ํ ์ ๋ฐฐ์น๋ฅผ ์์ํ์ง ์๊ณ ์ข
๋ฃ(์ด์/๋ช
์ธ ์กฐ์ ํ ์ฌ์คํ). " ๊ณ์ " ์ ํ ์ ์ฌ์ ๋ฅผ ๋ก๊ทธ์ ๋จ๊ธฐ๊ณ Phase 1 ๋ก ์งํ.
- post-batch: " ์ค๋จ " ์ ํ ์ Epic ์ข
๋ฃ๋ฅผ ๋ณด๋ฅ(Epic PR ์ ์ด๋ฏธ ๋ณํฉ๋์ผ๋ฏ๋ก ์ถ๊ฐ ํ์ ์ด์๋ก ํ๋ฅ ํญ๋ชฉ์ ์ถ์ ). " ๊ณ์ " ์ ํ ์ ๊ทธ๋๋ก ๋ง๋ฌด๋ฆฌ.
- ์ด ์ ๊ฒ์ **ํ์ง ๊ฒ์ดํธ๊ฐ ์๋๋ผ ๋ฐฉํฅ ์ ๊ฒ**์ด๋ค โ ํ
์คํธ/lint/๋ฆฌ๋ทฐ Critical ์ฒ๋ผ hard fail ํ์ง ์๊ณ , **MAJOR-DRIFT ์์๋ง ์ฌ์ฉ์ ํ์ธ์ผ๋ก ๋ฉ์ถ๋ค**.
0.5-4. ๋ฉฑ๋ฑ/์ฌ๊ฐ: ์ฝ๊ธฐ ์ ์ฉ์ด๋ผ ๋ถ์์ฉ์ด ์๋ค. `/dev:batch {epic}` ์ฌ์คํ ์ pre-batch ์ ๊ฒ๋ ๋ค์ ๋๋ ๊ฒ ์ ์์ด๋ฉฐ, ์ด๋ฏธ Closed ์ธ Story ๋ Phase 2 ์์ ๊ฑด๋๋ด๋ค.๋ฐฐ๊ฒฝ(D4): ๊ธธ๊ฒ ๋๋ batch ๊ฐ ํ Story ์ฉ ์ฒ๋ฆฌ๋๋ ๋์ ๋์ ๋๋ฉด "๊ฐ๋ณ PR ์ ํต๊ณผํ์ง๋ง Epic ์ ์ฒด๊ฐ ์๋ ๊ธฐํ ๋ชฉํ์์ ๋ฒ์ด๋" ์ํ๊ฐ ๋ ์ ์๋ค. ์ด checkpoint ๋ ๊ทธ ํ๋ฅ๋ฅผ ์์ ์์ ๊ณผ ๋ง๋ฌด๋ฆฌ ์์ ์ ์๋ฏธ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ฒ์ฉ ๋น์ถ๋ ๊ฑฐ์ธ์ด๋ค. ๋จ, ๊ธฐํ(Seed)์ด ์๋ ์ ์ฅ์์์๋ batch ๋ ๊ทธ๋๋ก ๋์ํด์ผ ํ๋ฏ๋ก ์๋ ๋ถ์ฌ/๋ฏธํ์ ์ ์์ ํ no-op ์ผ๋ก ๋๋ค(opt-in). ์ ๊ฒ ๋ก์ง์
/spec:status(D3) ์ ์์ํ๋ฉฐ ์ฌ๊ธฐ์ ์ด๋ค ๋น๊ต ์๊ณ ๋ฆฌ์ฆ๋ ์๋ก ๊ตฌํํ์ง ์๋๋ค.
Phase 1: Epic Branch Creation#
0. (Phase 0.5) Pre-batch seed-alignment checkpoint โ LOCKED seed ์์ผ๋ฉด LLM-SEMANTIC ์ ๋ ฌ ์ ๊ฒ(MAJOR-DRIFT ์ ํ์ธ ํ ์งํ), ์์ผ๋ฉด no-op skip
1. Query Epic issue info (ZenHub)
2. Update development branch to latest
3. Create Epic branch: epic/{epic_number}-{slug}
4. Move Epic to In Progress pipelinePhase 2: Sequential Story Processing (Per-Story Cycle)#
Executes the /dev:run cycle for each Story.
Base branch is automatically set to the Epic branch.
Per-Story Cycle:
1. Move Pipeline to " In Progress " (+ ํ์ฑ ์คํ๋ฆฐํธ ๋ฐฐ์ โ /dev:run Step 2.5 ์์, --no-sprint ๋ก opt-out)
2. Create Story branch: story/{story_number}-{slug} (based on Epic branch)
3. If Sub-tasks exist โ execute Per-Sub-task Cycle
4. If no Sub-tasks โ implement directly
5. Write and run tests
6. Pre-push verification (lint + DCM)
6.5. โญ **Per-Story Stall Budget** (verify์ review ์ฌ์ด): ๊ฐ์ ์คํจ๊ฐ ๋ฐ๋ณต๋๋ฉด 3-rung ๋๋๋ฅผ ์ค๋ฅธ๋ค โ (1) same-strategy retry(์ํญ ๋ฐ์ด๋), (2) ๋์ผ ์คํจ๊ฐ **์๋ฏธ์** ๋ฐ๋ณต๋๋ฉด affinity ํ๋ฅด์๋๋ก `/dev:unstuck solo` ํธ์ถ(reframe, ์ฌ์๋ ์๋), (3) ๊ทธ๋๋ ์ ํ๋ฆฌ๋ฉด ์ด Story๋ฅผ `BLOCKED( ' unstuck_exhausted ' )`๋ก ํ์ํ๊ณ **๋ค์ Story๋ก ๊ณ์**(Epic ์ ์ฒด ์ค๋จ ๊ธ์ง). ์์ธ๋ `sequential-workflow.md`์ " Stall Budget " ์ฐธ์กฐ.
7. Code Review Gate
8. Create PR โ targeting Epic branch (base: epic/{epic_number}-{slug}); PR body์ `Closes #{story_number}` + `Parent Epic: #{epic_number}` ํฌํจ
9. Squash merge (๋จธ์ง = Close) โ base๊ฐ **Epic ๋ธ๋์น(๋น๊ธฐ๋ณธ ๋ธ๋์น)** ์ด๋ฏ๋ก GitHub `Closes #`๋ **์๋์ผ๋ก ๋ซ์ง ์์ต๋๋ค**.
9.5. โญ **๋ช
์์ Close (ํ์ โ ํด๋ฐฑ ์๋)**: `gh issue close {story} --reason completed` ๋ก ์ง์ ๋ซ๊ธฐ โ `gh issue view {story} --json state`๋ก `CLOSED` ํ์ธ โ ZenHub `searchClosedIssues " #{story} " `์ ์์ผ๋ฉด `updateIssue state:CLOSED` (Done โ closed)
10. โญ Post-merge: `git checkout epic/{epic_number}-{slug} & & git pull --ff-only origin epic/{epic_number}-{slug} & & git submodule sync --recursive & & git submodule update --init --recursive` (base ์ต์ ํ + ์๋ธ๋ชจ๋ ๋๊ธฐํ) โ submodule sync/update ์คํจ๋ **๊ฒฝ๊ณ ํ ๊ณ์**(Epic ์ค๋จ ์๋). ์๋ธ๋ชจ๋ ์์ผ๋ฉด no-op.
11. Move to next StoryPer-Story Stall Budget (Step 6.5 detail)
A single Story that keeps failing must NOT halt the Epic. Between verification (Step 6) and the Code Review Gate (Step 7), apply the 3-rung stall ladder (shared with sequential-workflow.md โ "Stall Budget"):
| Rung | Trigger | Action |
|---|---|---|
| 1 | First failures | Same-strategy retry, small bounded count (lint/format/build auto-fix). |
| 2 | Same failure repeats (judged semantically, not by string diff) | /dev:unstuck solo <affinity-persona> โ reframe, not retry; next move must differ in kind. |
| 3 | Reframe exhausted, Story still stuck | Mark Story BLOCKED('unstuck_exhausted') โ CONTINUE to next Story. Epic NOT halted. |
Rung 2 affinity persona (semantic match, default CONTRARIAN): same-error-Nร โ hacker; flip-flop โ architect; missing-info โ researcher; over-built โ simplifier; unknown โ contrarian. Persona definitions: plugins/cc-spec-clarity/references/LATERAL_PERSONAS.md.
LLM-SEMANTIC, never lexical. "๋์ผ ์คํจ์ธ๊ฐ?" ํ์ ์ ์๋ฏธ ๊ธฐ๋ฐ โ ์๋ฌ ํ ์คํธ์ ํ ํฐ/๊ณต๋ฐฑ ๊ฒน์นจ์ผ๋ก ๋น๊ตํ์ง ์๋๋ค. ํ๊ตญ์ด๋ ๋์ด์ฐ๊ธฐ ๊ฒฝ๊ณ๊ฐ ์๊ณ ์กฐ์ฌ๊ฐ ๋ถ์ด ๊ฐ์ ๋ธ๋ก์ปค๋ฅผ ๊ฐ๋ฆฌํค๋ ๋ก๊ทธ๊ฐ ํ ํฐ์ ๊ฑฐ์ ๊ณต์ ํ์ง ์์ ์ ์๋ค.
/dev:unstuck์ ์ ํ์ง๋ฅผ ์ค ๋ฟ ๊ฒฐ๋ก ์ ๋ด์ง ์๋๋ค. BLOCKED ๋ Story ๋ Phase 0 ์ ๋ฉฑ๋ฑ resume ๊ณ์ฝ์ผ๋ก ๋์ค์ ๋ค์ ์๋๋๋ค.
Phase 2.5: Sequential Sub-task Processing (Optional)#
When a Story has Sub-tasks, process them with the same pattern.
Per-Sub-task Cycle:
1. Create Sub-task branch: task/{subtask_number}-{slug} (based on Story branch)
2. Implementation work
3. Write and run tests
4. Verification gate
5. Create PR โ targeting Story branch (base: story/{story_number}-{slug}); PR body์ `Closes #{subtask_number}` + `Parent Story: #{story_number}` ํฌํจ
6. Squash merge (๋จธ์ง = Close) โ base๊ฐ **Story ๋ธ๋์น(๋น๊ธฐ๋ณธ ๋ธ๋์น)** ์ด๋ฏ๋ก GitHub `Closes #`๋ **์๋์ผ๋ก ๋ซ์ง ์์ต๋๋ค**.
6.5. โญ **๋ช
์์ Close (ํ์ โ ํด๋ฐฑ ์๋)**: `gh issue close {subtask} --reason completed` ๋ก ์ง์ ๋ซ๊ธฐ โ `CLOSED` ํ์ธ โ ZenHub ๋ฏธ๋๊ธฐํ ์ `updateIssue state:CLOSED`
7. โญ Post-merge: `git checkout story/{story_number}-{slug} & & git pull --ff-only origin story/{story_number}-{slug} & & git submodule sync --recursive & & git submodule update --init --recursive` (base ์ต์ ํ + ์๋ธ๋ชจ๋ ๋๊ธฐํ)
8. Move to next Sub-taskPhase 3: Epic PR Creation#
After all Stories are complete:
1. โ ์๋ฃ์ฑ Hard Gate (ํ์, ์คํํ) โ ๋ชจ๋ ํ์ StoryยทSub-task ์ด์๊ฐ ์ ๋ง Closed ์ธ์ง ํ์ธ. prose ํ์ธ ๊ธ์ง:
const children = await mcp__zenhub__searchLatestIssues({ query: `parent:${epicId}` }); // getChildrenOfParent ์์
const open = children.filter(c = > (c.state || " " ).toUpperCase() !== " CLOSED " ); // GitHub state = source of truth
if (children.length === 0) โ " ํ์ ๋ถ๊ฐ " : ์์์ด ์๋ ๊ฒ ๋ง๋์ง ์ฌ์ฉ์ ํ์ธ ํ์๋ง ์งํ
if (open.length > 0) โ โ Epic PR/๋จธ์ง/์ข
๋ฃ ์ค๋จ. BLOCKED( ' unstuck_exhausted ' ) ๋ OPEN ์ผ๋ก ๊ฐ์ฃผ.
throw `Epic #${epic} ๋ฏธ์๋ฃ: OPEN child ${open.length}๊ฐ (${open.map(c= > " # " +c.number)}). ํด๊ฒฐ/์ฌ์๋ ํ batch ์ฌ์คํ`
2. Review changes against development
3. Create Epic PR โ targeting development; PR body์ `Closes #{epic_number}` ํฌํจ
4. Conduct code review
5. Squash merge after user approval (๋จธ์ง = Close)
6. **Epic ์ด์ ์ข
๋ฃ** โ `development`๊ฐ ์ ์ฅ์ **๊ธฐ๋ณธ(default) ๋ธ๋์น**๋ฉด `Closes #{epic_number}`๋ก ์๋ ๋ซํ, ์๋๋ฉด ์๋์ผ๋ก ๋ซํ์ง ์์ผ๋ฏ๋ก ๋ช
์์ ์ข
๋ฃ ํ์.
6.5. โญ Close ๊ฒ์ฆ + ํด๋ฐฑ: `gh issue view {epic} --json state` โ ๋ฏธclose ์ `gh issue close {epic} --reason completed`; ZenHub ๋ฏธ๋๊ธฐํ ์ `updateIssue state:CLOSED`
6.6. โญ **Project ์ฌ๊ท ํ์ธ** โ Epic์ด Project(๋๋ Initiative)์ ์์์ด๋ฉด, `checkAndCloseParent(epic_number)`(`commands/agents/dev/issue-state-agent.md`)๋ฅผ ํธ์ถํด ํ์ Epic์ด ๋ชจ๋ Closed์ธ์ง ํ์ธ ํ ์์๋ฅผ ์๋ ์ข
๋ฃ(์ฐ์์ ์ผ๋ก Initiative๊น์ง). Epic์ด ์ต์์(๋ถ๋ชจ ์์)๋ฉด no-op.
7. โญ Post-merge: `git checkout development & & git pull --ff-only origin development & & git submodule sync --recursive & & git submodule update --init --recursive` (base ์ต์ ํ + ์๋ธ๋ชจ๋ ๋๊ธฐํ)
8. (Phase 0.5) Post-batch seed-alignment checkpoint โ LOCKED seed ์์ผ๋ฉด ์๋ฃ๋ ์ด์ ์งํฉ + Epic PR ์ LLM-SEMANTIC ์ผ๋ก ์ฌ๋์กฐ(AC-ID ์ํ echo + ๋ชฉํ ์ ๋ ฌ ํ์ ), MAJOR-DRIFT ์ ๊ฒฝ๊ณ ํ Epic ์ข
๋ฃ ํ์ธ. ์์ผ๋ฉด no-op skip.Flow Diagram#
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ /dev:batch {epic_number} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Phase 1: Epic Branch Creation โ
โ โโโ Query Epic issue (ZenHub) โ
โ โโโ git checkout development & & git pull โ
โ โโโ git checkout -b epic/{number}-{slug} โ
โ โโโ git push -u origin epic/{number}-{slug} โ
โ โ
โ Phase 2: Sequential Story Processing โ
โ โโโ Query Story list (child issues) โ
โ โโโ for each Story: โ
โ โ โโโ /dev:run --base epic/{number}-{slug} {story_num} โ
โ โ โ โโโ Branch: story/{story}-{slug} โ
โ โ โ โโโ [If Sub-tasks exist] โ
โ โ โ โ โโโ Per-Sub-task Cycle โ
โ โ โ โ โโโ task/{subtask}-{slug} (Story-based) โ
โ โ โ โ โโโ Implement โ Test โ Verify โ
โ โ โ โ โโโ PR โ Merge into Story branch โ
โ โ โ โโโ Implement โ Test โ Verification Gate โ
โ โ โ โโโ PR โ Merge into Epic branch โ
โ โ โโโ Next Story โ
โ โ โ
โ Phase 3: Epic PR Creation โ
โ โโโ Create Epic branch โ development PR โ
โ โโโ Review full changes โ
โ โโโ Wait for user approval โ
โ โโโ Squash merge โ Epic Close โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโMCP Call Order#
// Phase 1: Initialization
const workspace = await mcp__zenhub__getWorkspacePipelinesAndRepositories();
const epic = await mcp__zenhub__searchLatestIssues({ query: `#${epicNumber}` });
const stories = await mcp__zenhub__searchLatestIssues({
query: `parent:${epic[0].id}`,
});
// Phase 2: Per-Story processing
for (const story of stories) {
// Check Sub-tasks
const subtasks = await mcp__zenhub__searchLatestIssues({
query: `parent:${story.id}`,
});
if (subtasks.length > 0) {
// Story with Sub-tasks โ create Story branch first
// Process Sub-tasks sequentially, then integrate into Story branch
for (const subtask of subtasks) {
// /dev:run --base story/{story_number}-{slug} {subtask.number}
}
// PR Story branch to Epic branch
} else {
// Story without Sub-tasks โ implement directly
// /dev:run --base epic/{epic_number}-{slug} {story.number}
}
}
// Phase 3: Epic PR
// gh pr create --base development --title " [Epic] ... "Pipeline Query#
Pipeline IDs differ per workspace. Dynamic query at session start:
const workspace = await mcp__zenhub__getWorkspacePipelinesAndRepositories();
const pipelines = workspace.pipelines;
// Find by name in pipelines: " New Issues " , " In Progress " , " Review/QA " , etc.
// Done pipeline is NOT used (Done โ closed). Merge = Close: `Closes #` โ GitHub closed
// โ ZenHub syncs to Closed. Verify with searchClosedIssues + fallback updateIssue.PR Creation Templates#
Story PR (โ Epic branch)#
gh pr create --base " epic/{epic_number}-{slug} " \
--title " {story.title} " --body " $(cat < < ' EOF '
## Summary
- {Change summary}
## Related Issue
- Closes #{story.number}
- Parent Epic: #{epic_number}
## Test Plan
- [ ] UseCase unit tests passed
- [ ] BLoC unit tests passed
๐ค Generated with [Claude Code](https://claude.ai/claude-code)
EOF
) "Sub-task PR (โ Story branch)#
gh pr create --base " story/{story_number}-{slug} " \
--title " {subtask.title} " --body " $(cat < < ' EOF '
## Summary
- {Change summary}
## Related Issue
- Closes #{subtask_number}
- Parent Story: #{story_number}
## Test Plan
- [ ] Unit tests passed
๐ค Generated with [Claude Code](https://claude.ai/claude-code)
EOF
) "Epic PR (โ development)#
gh pr create --base " development " \
--title " [Epic] {epic.title} " --body " $(cat < < ' EOF '
## Summary
- {Epic full change summary}
## Included Stories
- โ
#{story_1} - {title}
- โ
#{story_2} - {title}
- โ
#{story_3} - {title}
## Related Issue
- Closes #{epic_number}
## Test Plan
- [ ] Full integration tests passed
- [ ] Manual testing complete
๐ค Generated with [Claude Code](https://claude.ai/claude-code)
EOF
) "Issue Closure & Post-Merge Sync โญ#
๋ชจ๋ ๋จธ์ง๋ ํด๋น ์ด์๋ฅผ Close(๊ฒ์ฆ ํฌํจ)ํ๊ณ , base ๋ธ๋์น๋ฅผ ์ต์ ์ผ๋ก ๋๊ธฐํํ๋ค. ์ ์ฑ = "๋จธ์ง = Close" (B).
โ ๏ธ ZenHub 2-์ํ ๋ชจ๋ธ: Pipeline(๋ณด๋ ์นผ๋ผ)๊ณผ GitHub state(open/closed)๋ ๋ณ๊ฐ๋ค.
Done์นผ๋ผ์ผ๋ก ์ฎ๊ฒจ๋ GitHub ์ด์๋open๊ทธ๋๋ก โDoneโ closed.Closedํ์ดํ๋ผ์ธ๋ง GitHubclosed์ 1:1. ๋ฐ๋ผ์Done์ ์ฐ์ง ์๊ณ , ๋จธ์ง ํ GitHub state๋ก ๋ซํ์ ๊ฒ์ฆํ๊ณ ํด๋ฐฑํ๋ค.
| ๋จ๊ณ | ๋จธ์ง ๋ฐฉํฅ | ์ด์ Close (๊ฒ์ฆ ํฌํจ) | Post-merge ์ฒดํฌ์์ |
|---|---|---|---|
| Sub-task | โ Story ๋ธ๋์น | Sub-task ์ด์ Close (Closes #{subtask}) + verify_closed | git checkout story/{story}-{slug} && git pull + git submodule update --init --recursive |
| Story | โ Epic ๋ธ๋์น | Story ์ด์ Close (Closes #{story}) + verify_closed | git checkout epic/{epic}-{slug} && git pull + git submodule update --init --recursive |
| Epic | โ development | Epic ์ด์ ์ข
๋ฃ (Closes #{epic}) + verify_closed | git checkout development && git pull + git submodule update --init --recursive |
- ๊ฐ PR body์
Closes #{number}ํค์๋๋ก squash ๋จธ์ง ์ GitHub ์ด์๊ฐ ์๋ Close๋๊ณ , ์ฐ๋๋ ZenHub ์ด์ ์ํ๋ Closed๋ก ๋๊ธฐํ๋๋ค. - Close ๊ฒ์ฆ + ํด๋ฐฑ (๋งค ๋จธ์ง ํ ํ์):
gh issue view {N} --json state๋ก GitHubclosedํ์ธ โ ์๋๋ฉดgh issue close {N} --reason completed. ์ด์ดsearchClosedIssues "#{N}"๋ก ZenHub ๋๊ธฐํ ํ์ธ โ ๋๋ฝ ์updateIssue state:CLOSED. (Closes #N๋๋ฝยท๋๊ธฐํ ์ง์ฐ ๋๋น) - ํ์ StoryยทSub-task๊ฐ ๋ชจ๋ Close๋๊ณ Epic ๋ธ๋์น PR์ด development์ ๋จธ์ง๋๋ฉด Epic ์ด์๊ฐ ์๋ ์ข ๋ฃ๋๋ฉฐ, development๋ฅผ ์ต์ ์ผ๋ก ์ฒดํฌ์์ํ๋ค.
- ์๋ธ๋ชจ๋ ๋๊ธฐํ (๋งค ๋จธ์ง ํ ํ์):
git pull์ superproject์ ์๋ธ๋ชจ๋ ํฌ์ธํฐ๋ง ๊ฐฑ์ ํ๊ณ ์๋ธ๋ชจ๋ ์ํนํธ๋ฆฌ๋ ์ฒดํฌ์์ํ์ง ์๋๋ค. base ์ต์ ํ ์งํgit submodule sync --recursive && git submodule update --init --recursive๋ก ์๋ธ๋ชจ๋์ base๊ฐ ๊ฐ๋ฆฌํค๋ ์ปค๋ฐ์ ์ ๋ ฌํ๋ค(Serverpod/Melos ๋ชจ๋ ธ๋ ํฌ ๋น๋/์ฝ๋์ drift ๋ฐฉ์ง). ์๋ธ๋ชจ๋์ด ์์ผ๋ฉด no-op. - ๋จธ์ง ํ base ์ต์ ํ + ์๋ธ๋ชจ๋ ๋๊ธฐํ๋
/dev:runStep 12.5(Closure Verification + Post-Merge Sync)์ ๋์ผ ์์น(git pull --ff-only+git submodule update)์ด๋ค.
Verification Checklist#
- (seed๊ฐ LOCKED์ผ ๋๋ง) pre-batch / post-batch seed-alignment ์ ๊ฒ ์คํ, MAJOR-DRIFT ์ ์ฌ์ฉ์ ํ์ธ โ seed ๋ถ์ฌ/DRAFT๋ฉด no-op skip (์ฒดํฌ ๋ถํ์)
- Epic branch correctly branched from development
- Each Story branch branched from Epic branch
- Sub-task branches branched from Story branch
- Story PR base is Epic branch
- Epic PR base is development
- Epic PR created after all Stories are merged
- ๋ฐ๋ณต ์คํจ Story๋ stall budget(retry โ
/dev:unstuck soloโ BLOCKED)์ ์ ์ฉํ๊ณ , BLOCKED์ฌ๋ Epic์ ๊ณ์ ์งํ - ๊ฐ Sub-task/Story PR์ด
Closes #{number}๋ก ์ด์๋ฅผ Close - ๊ฐ ๋จธ์ง ํ GitHub
closed+ ZenHubClosed๊ฒ์ฆ (verify_closed, Done์ ๋ฐฉ์น ๊ธ์ง) - Epic PR ๋จธ์ง ์ Epic ์ด์ ์๋ ์ข ๋ฃ + ๊ฒ์ฆ
- Epic์ด Project/Initiative์ ์์์ด๋ฉด
checkAndCloseParent๋ก ์์๊น์ง ์ฌ๊ท ํ์ธ - ๊ฐ ๋จธ์ง ํ base ๋ธ๋์น ์ต์ ์ฒดํฌ์์ (
git pull) - BDD scenarios vs actual implementation comparison
- Code conventions followed
- Lint 0 issues + DCM error 0 issues