| name | sg-parallel-execute |
| description | Use this when parallel_groups.json exists and independent plan groups should run concurrently โ dispatches up to 3 Task() agents, one per group, without calling superpowers:executing-plans. |
Detect the user's input language and respond in that language throughout this skill's output.
- Korean input โ respond in Korean
- English input โ respond in English
- Mixed input โ match the dominant language
Read the parallel_groups.json file path passed via $ARGUMENTS using the Read tool to identify the group list. Execute one wave at a time: for each wave (lowest wave number first), dispatch up to 3 groups from that wave as concurrent Task()s, wait for all to complete successfully, then advance to the next wave. Never dispatch groups from different waves in the same parallel batch โ wave numbers are dependency barriers, not sort keys. Each Task() reads its group's PLAN.md directly and executes tasks without calling superpowers:executing-plans.
<execution_context>
Self-contained. Reads $ARGUMENTS (parallel_groups.json path), then reads each group's PLAN.md files. Does not write to .planning/STATE.md or any GSD/Superpowers internal files. On failure, appends a parallel-failed row to .planning/HANDOFF.md so the idempotency guard in sg-execute can detect the failed state and allow re-execution. Parallel Task() agents are dispatched in the same response.
</execution_context>
Step 1 โ Input validation.
If $ARGUMENTS is empty, print an error message and exit:
[sg-parallel-execute] Error: $ARGUMENTS must be the path to parallel_groups.json. Got empty.
GROUPS_JSON_FILE="$ARGUMENTS"
if [ -z "$GROUPS_JSON_FILE" ]; then
echo "[sg-parallel-execute] Error: \$ARGUMENTS must be the path to parallel_groups.json. Got empty."
exit 1
fi
Step 1.5 โ Phase ๋ฒํธ โ ํ์ผ ๊ฒฝ๋ก ๋ณํ (์ ํ์ ).
$ARGUMENTS๊ฐ ์ซ์ ํจํด(^[0-9]+(\.[0-9]+)?$)์ ๋งค์นญ๋๋ฉด phase ๋ฒํธ๋ก ํด์ํ๊ณ , parallel_groups.json ํ์ผ ๊ฒฝ๋ก๋ฅผ ์๋ ํ์ํ๋ค. ํ์ผ ๊ฒฝ๋ก๊ฐ ์ง์ ์ ๋ฌ๋ ๊ฒฝ์ฐ์๋ ์ด ์คํ
์ ๊ฑด๋๋ด๋ค.
GROUPS_JSON_FILE="$ARGUMENTS"
if echo "$GROUPS_JSON_FILE" | grep -qE '^[0-9]+(\.[0-9]+)?$'; then
INT_PART=$(echo "$GROUPS_JSON_FILE" | cut -d. -f1)
DEC_PART=$(echo "$GROUPS_JSON_FILE" | grep -oE '\.[0-9]+$' || true)
PHASE_PAD=$(printf "%02d" "$INT_PART")${DEC_PART}
FOUND=$(ls .planning/phases/${PHASE_PAD}-*/parallel_groups.json 2>/dev/null | head -2)
FOUND_COUNT=$(echo "$FOUND" | grep -c . 2>/dev/null || echo 0)
if [ -z "$FOUND" ] || [ "$FOUND_COUNT" -eq 0 ]; then
echo "[sg-parallel-execute] Error: No parallel_groups.json found for phase ${PHASE_PAD} under .planning/phases/"
exit 1
fi
if [ "$FOUND_COUNT" -gt 1 ]; then
GROUPS_JSON_FILE=$(echo "$FOUND" | head -1)
echo "[sg-parallel-execute] Warning: Multiple matches found for phase ${PHASE_PAD}; using $GROUPS_JSON_FILE"
else
GROUPS_JSON_FILE="$FOUND"
fi
fi
Step 1.6 โ parallel_groups.json ์๋ ์์ฑ (ํ์ผ ์์ ๋).
$GROUPS_JSON_FILE ๊ฒฝ๋ก์ ํ์ผ์ด ์กด์ฌํ์ง ์์ผ๋ฉด PHASE_DIR์ PLAN.md ํ์ผ๋ค์ ์ฝ์ด ์๋์ผ๋ก ์์ฑํ๋ค.
if [ ! -f "$GROUPS_JSON_FILE" ]; then
PHASE_DIR=$(dirname "$GROUPS_JSON_FILE")
PLAN_FILES=$(ls "$PHASE_DIR"/*-PLAN.md 2>/dev/null | sort)
if [ -z "$PLAN_FILES" ]; then
echo "[sg-parallel-execute] Error: No PLAN.md files found in $PHASE_DIR โ cannot auto-generate parallel_groups.json"
exit 1
fi
echo "[sg-parallel-execute] parallel_groups.json not found โ auto-generating from PLAN.md files in $PHASE_DIR"
fi
ํ์ผ์ด ์์ผ๋ฉด ๊ฐ PLAN.md๋ฅผ Read tool๋ก ์ฝ์ด frontmatter์ wave: ๊ฐ์ ์ถ์ถํ๋ค (์์ผ๋ฉด ๊ธฐ๋ณธ๊ฐ 1). wave ๋ฒํธ๋ณ๋ก ํ๋์ ๊ทธ๋ฃนํํด ์๋ ํ์์ JSON ๋ฐฐ์ด์ ๊ตฌ์ฑํ๋ค:
[
{"wave": 1, "plans": ["NN-01-PLAN.md", "NN-02-PLAN.md"], "merged": false},
{"wave": 2, "plans": ["NN-03-PLAN.md"], "merged": false}
]
๊ตฌ์ฑ๋ JSON์ $GROUPS_JSON_FILE ๊ฒฝ๋ก์ Write tool๋ก ์ ์ฅํ ๋ค ์๋ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ค:
[sg-parallel-execute] Auto-generated parallel_groups.json with <N> group(s) across <W> wave(s)
ํ์ผ์ด ์ด๋ฏธ ์กด์ฌํ๋ฉด ์ด ์คํ
์ ์ฒด๋ฅผ ๊ฑด๋๋ด๋ค.
Step 2 โ Read parallel_groups.json.
Read the file at the $GROUPS_JSON_FILE path using the Read tool. If the file cannot be parsed as a JSON array, print an error message and exit:
[sg-parallel-execute] Error: Cannot parse parallel_groups.json at <path> as JSON array.
Parse the content read by the Read tool as a JSON array. Each parsed item has the shape {"wave": N, "plans": ["NN-01-PLAN.md", ...], "merged": false}.
Step 3 โ Compute GROUP_COUNT and determine execution groups.
Length of parsed array = GROUP_COUNT.
If GROUP_COUNT is 0, print an error message and exit (D-07, no automatic fallback):
[sg-parallel-execute] Error: parallel_groups.json contains zero groups. Nothing to execute.
CURRENT_WAVE = lowest wave number among all groups.
WAVE_GROUPS = groups where wave == CURRENT_WAVE.
EXEC_COUNT = min(len(WAVE_GROUPS), 3).
OVERFLOW_GROUPS = WAVE_GROUPS[3:] if len(WAVE_GROUPS) > 3 else [].
REMAINING_WAVES = groups where wave > CURRENT_WAVE, sorted by wave ascending.
Output:
[sg-parallel-execute] GROUP_COUNT=N, CURRENT_WAVE=W, EXEC_COUNT=M
Step 4 โ Read PLAN.md for each parallel group.
Determine PHASE_DIR from the directory portion of the $GROUPS_JSON_FILE path (e.g. if $GROUPS_JSON_FILE is .planning/phases/18-sg-parallel-execute/parallel_groups.json, then PHASE_DIR = .planning/phases/18-sg-parallel-execute).
Extract PHASE_NUM from the first number before - in the PHASE_DIR directory name:
PHASE_NUM=$(basename "$PHASE_DIR" | sed -E 's/^([0-9]+)-.*/\1/')
(e.g. 18-sg-parallel-execute โ 18, 9-foo โ 9, 100-bar โ 100)
Iterate over the filenames in each group's plans array and read each PLAN.md body using the Read tool:
- PLAN.md path =
{PHASE_DIR}/{plan_filename}
Step 5 โ Parallel Task() dispatch.
Execute EXEC_COUNT Task()s in parallel within the same response (D-02, TE-02a). Prompt structure for each Task() (D-03):
Execute the following plan(s) for Phase {PHASE_NUM}.
CRITICAL constraints โ do NOT violate these:
- Do NOT call superpowers:executing-plans (bare task execution only)
- Do NOT write to .planning/HANDOFF.md
- Do NOT update .planning/STATE.md
- Do NOT modify any GSD or Superpowers internal files (non-invasive rule)
Plans to execute:
=== {plan_filename} ===
{full PLAN.md body}
Execute all tasks in the plan(s) above. Follow each task's <action>, <verify>, and <done> fields. Report completion status for each task.
When a group contains multiple plan files, include them all in the same Task() prompt.
After the parallel batch completes: if any Task() reports failure, append a parallel-failed row to .planning/HANDOFF.md and stop:
| <timestamp> | <phase-slug> | parallel | parallel-failed | - |
Then surface the error with a retry instruction:
[sg-parallel-execute] FAILED: Wave W โ <error summary>
[sg-parallel-execute] Partial changes may exist. Fix the failing plan(s) and re-run /super-gsd:sg-execute <phase> to retry.
If all Task()s succeed and OVERFLOW_GROUPS is non-empty, execute each group sequentially (one Task() at a time) before advancing to Step 6.
Step 6 โ Advance through remaining waves.
After the parallel batch (Step 5) and any OVERFLOW_GROUPS complete successfully, process REMAINING_WAVES one wave at a time in ascending order. Do not start a wave until all Task()s from the previous wave have completed successfully. If any Task() reports failure, append a parallel-failed row to .planning/HANDOFF.md (same format as Step 5), surface the error with the retry instruction, and stop instead of advancing.
For each subsequent wave:
- CURRENT_WAVE = next wave number from REMAINING_WAVES.
- WAVE_GROUPS = groups for this wave. EXEC_COUNT = min(len(WAVE_GROUPS), 3). OVERFLOW_GROUPS = WAVE_GROUPS[3:] if len(WAVE_GROUPS) > 3 else [].
- Read PLAN.md for each group in WAVE_GROUPS using the Read tool (PLAN.md path =
{PHASE_DIR}/{plan_filename}).
- Dispatch EXEC_COUNT Task()s in parallel. Wait for all to complete before advancing.
- If OVERFLOW_GROUPS is non-empty, execute each group sequentially (one Task() at a time) before advancing to the next wave.
[sg-parallel-execute] Wave {W}: dispatching {EXEC_COUNT} group(s) in parallel
<success_criteria>
- When a valid parallel_groups.json path is passed via $ARGUMENTS, read the file with the Read tool and output GROUP_COUNT.
- Groups are processed one wave at a time (lowest wave first). Groups from different waves are never dispatched in the same parallel batch.
- Within each wave, up to 3 groups are dispatched as concurrent Task()s. No subsequent wave starts until all Task()s in the current wave complete successfully.
- Each Task() prompt includes instructions prohibiting: calling superpowers:executing-plans, writing to HANDOFF.md, and updating STATE.md.
- If $ARGUMENTS is empty, print an error message and exit. If parallel_groups.json does not exist, auto-generate it from PLAN.md wave fields before proceeding.
- On any Task() failure, append a parallel-failed row to HANDOFF.md, surface a retry instruction, and stop โ never advance to the next wave after a failure.
</success_criteria>