refactor(10): simplify plan frontmatter for parallelization
Remove confusing `parallelizable` boolean field. It was redundant - execute-phase already computes parallelizability from `depends_on` and file conflicts. Changes: - Remove `parallelizable: true|false` from plan frontmatter - Rename `files_exclusive` to `files_modified` (clearer intent) - Simplify execute-phase dependency detection logic - Update phase-prompt template and plan-phase workflow Parallelization is now fully automatic based on: - `depends_on: []` (empty = independent) - `files_modified: [...]` (no overlap = can parallelize) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,24 +13,11 @@ Template for `.planning/phases/XX-name/{phase}-{plan}-PLAN.md` - executable phas
|
||||
phase: XX-name
|
||||
plan: NN
|
||||
type: execute
|
||||
parallelizable: true|false # Can run alongside other parallelizable plans
|
||||
depends_on: [] # Explicit plan dependencies (e.g., ["11-01", "11-02"])
|
||||
files_exclusive: [] # Files only this plan modifies (from <files> elements)
|
||||
depends_on: [] # Plan IDs this plan requires (e.g., ["01-01"]). Empty = independent.
|
||||
files_modified: [] # Files this plan modifies (from <files> elements)
|
||||
domain: [optional - if domain skill loaded]
|
||||
---
|
||||
|
||||
<frontmatter_guidance>
|
||||
**Parallelization fields:**
|
||||
- `parallelizable`: Set to true if plan has no dependencies and doesn't share files with sibling plans. Default false for safety.
|
||||
- `depends_on`: Array of plan IDs this plan requires (e.g., `["11-01", "11-02"]`). Empty array means independent.
|
||||
- `files_exclusive`: Files from `<files>` elements that no sibling plan touches. Used for conflict detection by execute-phase.
|
||||
|
||||
**When to set parallelizable: true:**
|
||||
- Plan has no depends_on entries
|
||||
- All files in plan are in files_exclusive (no overlap with sibling plans)
|
||||
- Plan doesn't consume outputs from sibling plans in same phase
|
||||
</frontmatter_guidance>
|
||||
|
||||
<objective>
|
||||
[What this phase accomplishes - from roadmap phase goal]
|
||||
|
||||
@@ -209,23 +196,21 @@ See `~/.claude/get-shit-done/references/tdd.md` for TDD plan structure.
|
||||
phase: 01-foundation
|
||||
plan: 02
|
||||
type: execute
|
||||
parallelizable: false
|
||||
depends_on: ["01-01"]
|
||||
files_exclusive: [src/app/api/auth/login/route.ts]
|
||||
files_modified: [src/app/api/auth/login/route.ts]
|
||||
domain: next-js
|
||||
---
|
||||
```
|
||||
|
||||
**Parallel plan (independent):**
|
||||
**Independent plan (can run parallel):**
|
||||
|
||||
```markdown
|
||||
---
|
||||
phase: 03-features
|
||||
plan: 02
|
||||
type: execute
|
||||
parallelizable: true
|
||||
depends_on: []
|
||||
files_exclusive: [src/components/Dashboard.tsx, src/hooks/useDashboard.ts]
|
||||
files_modified: [src/components/Dashboard.tsx, src/hooks/useDashboard.ts]
|
||||
---
|
||||
```
|
||||
|
||||
@@ -236,9 +221,8 @@ files_exclusive: [src/components/Dashboard.tsx, src/hooks/useDashboard.ts]
|
||||
phase: 01-foundation
|
||||
plan: 01
|
||||
type: execute
|
||||
parallelizable: true
|
||||
depends_on: []
|
||||
files_exclusive: [prisma/schema.prisma, src/lib/db.ts]
|
||||
files_modified: [prisma/schema.prisma, src/lib/db.ts]
|
||||
domain: next-js
|
||||
---
|
||||
|
||||
@@ -301,16 +285,15 @@ After completion, create `.planning/phases/01-foundation/01-01-SUMMARY.md`
|
||||
</output>
|
||||
```
|
||||
|
||||
**Parallel-aware plan example (independent):**
|
||||
**Independent plan example:**
|
||||
|
||||
```markdown
|
||||
---
|
||||
phase: 05-features
|
||||
plan: 01
|
||||
type: execute
|
||||
parallelizable: true
|
||||
depends_on: []
|
||||
files_exclusive: [src/features/user/model.ts, src/features/user/api.ts, src/features/user/UserList.tsx]
|
||||
files_modified: [src/features/user/model.ts, src/features/user/api.ts, src/features/user/UserList.tsx]
|
||||
---
|
||||
|
||||
<objective>
|
||||
@@ -323,21 +306,19 @@ Output: User model, API endpoints, and UI components.
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
# No SUMMARY references - this plan is independent
|
||||
</context>
|
||||
...
|
||||
```
|
||||
|
||||
**Sequential plan example (has dependencies):**
|
||||
**Dependent plan example:**
|
||||
|
||||
```markdown
|
||||
---
|
||||
phase: 06-integration
|
||||
plan: 02
|
||||
type: execute
|
||||
parallelizable: false
|
||||
depends_on: ["06-01"]
|
||||
files_exclusive: [src/integration/stripe.ts]
|
||||
files_modified: [src/integration/stripe.ts]
|
||||
---
|
||||
|
||||
<objective>
|
||||
@@ -350,14 +331,15 @@ Output: Stripe integration with user-linked payments.
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/phases/06-integration/06-01-SUMMARY.md # Needed: auth decisions
|
||||
@.planning/phases/06-integration/06-01-SUMMARY.md
|
||||
</context>
|
||||
...
|
||||
```
|
||||
|
||||
**Key differences:**
|
||||
- Parallel: `parallelizable: true`, empty `depends_on`, no SUMMARY refs
|
||||
- Sequential: `parallelizable: false`, explicit `depends_on`, includes needed SUMMARY
|
||||
**Parallelization rules:**
|
||||
- Empty `depends_on` + no file conflicts with sibling plans = can run parallel
|
||||
- Non-empty `depends_on` OR shared files = must run sequentially
|
||||
- `/gsd:execute-phase` analyzes this automatically
|
||||
|
||||
</good_examples>
|
||||
|
||||
|
||||
@@ -121,58 +121,43 @@ echo "Found ${#UNEXECUTED[@]} unexecuted plans"
|
||||
|
||||
```bash
|
||||
# Initialize associative arrays for tracking
|
||||
declare -A PLAN_REQUIRES # plan -> required plans
|
||||
declare -A PLAN_REQUIRES # plan -> required plans (from depends_on or inferred)
|
||||
declare -A PLAN_FILES # plan -> files modified
|
||||
declare -A PLAN_CHECKPOINTS # plan -> has checkpoints
|
||||
declare -A PLAN_PARALLELIZABLE # plan -> explicit parallelizable flag (Phase 11+)
|
||||
declare -A PLAN_DEPENDS_ON # plan -> explicit depends_on list (Phase 11+)
|
||||
declare -A PLAN_FILES_EXCLUSIVE # plan -> explicit file ownership (Phase 11+)
|
||||
declare -A PLAN_HAS_FRONTMATTER # plan -> whether has new frontmatter
|
||||
|
||||
for plan in "${UNEXECUTED[@]}"; do
|
||||
plan_id=$(basename "$plan" -PLAN.md)
|
||||
|
||||
# NEW: Check for parallelization frontmatter (Phase 11+)
|
||||
PARALLELIZABLE=$(awk '/^---$/,/^---$/' "$plan" | grep "^parallelizable:" | awk '{print $2}')
|
||||
# Check for depends_on frontmatter
|
||||
DEPENDS_ON=$(awk '/^---$/,/^---$/' "$plan" | grep "^depends_on:" | sed 's/depends_on: \[//' | sed 's/\]//' | tr -d ' "')
|
||||
FILES_EXCLUSIVE=$(awk '/^---$/,/^---$/' "$plan" | grep "^files_exclusive:" | sed 's/files_exclusive: \[//' | sed 's/\]//' | tr -d ' "')
|
||||
|
||||
# If frontmatter fields exist, use them directly
|
||||
if [ -n "$PARALLELIZABLE" ]; then
|
||||
PLAN_HAS_FRONTMATTER["$plan_id"]="true"
|
||||
PLAN_PARALLELIZABLE["$plan_id"]="$PARALLELIZABLE"
|
||||
PLAN_DEPENDS_ON["$plan_id"]="$DEPENDS_ON"
|
||||
PLAN_FILES_EXCLUSIVE["$plan_id"]="$FILES_EXCLUSIVE"
|
||||
# Check for files_modified frontmatter
|
||||
FILES_MODIFIED=$(awk '/^---$/,/^---$/' "$plan" | grep "^files_modified:" | sed 's/files_modified: \[//' | sed 's/\]//' | tr -d ' "')
|
||||
|
||||
# Use files_exclusive as PLAN_FILES when present
|
||||
if [ -n "$FILES_EXCLUSIVE" ]; then
|
||||
PLAN_FILES["$plan_id"]="$FILES_EXCLUSIVE"
|
||||
fi
|
||||
|
||||
# Use depends_on as PLAN_REQUIRES when present
|
||||
if [ -n "$DEPENDS_ON" ]; then
|
||||
PLAN_REQUIRES["$plan_id"]="$DEPENDS_ON"
|
||||
fi
|
||||
# Use frontmatter if present
|
||||
if [ -n "$DEPENDS_ON" ]; then
|
||||
PLAN_REQUIRES["$plan_id"]="$DEPENDS_ON"
|
||||
else
|
||||
PLAN_HAS_FRONTMATTER["$plan_id"]="false"
|
||||
|
||||
# Fall back to inference (existing logic)
|
||||
# Extract frontmatter requires (handles YAML array syntax)
|
||||
# Fall back to inference from old frontmatter format
|
||||
REQUIRES=$(awk '/^---$/,/^---$/' "$plan" | grep -E "^\s*-\s*phase:" | grep -oP '\d+' | tr '\n' ',')
|
||||
PLAN_REQUIRES["$plan_id"]="${REQUIRES%,}"
|
||||
|
||||
# Extract files from <files> elements (all occurrences)
|
||||
FILES=$(grep -oP '(?<=<files>)[^<]+(?=</files>)' "$plan" | tr '\n' ',' | tr -d ' ')
|
||||
PLAN_FILES["$plan_id"]="${FILES%,}"
|
||||
|
||||
# Check for SUMMARY references in @context
|
||||
# Check for SUMMARY references in @context (implies dependency)
|
||||
SUMMARY_REFS=$(grep -oP '@[^@]*\d+-\d+-SUMMARY\.md' "$plan" | grep -oP '\d+-\d+' | tr '\n' ',')
|
||||
if [ -n "$SUMMARY_REFS" ]; then
|
||||
PLAN_REQUIRES["$plan_id"]="${PLAN_REQUIRES[$plan_id]},${SUMMARY_REFS%,}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for checkpoint tasks (always check, regardless of frontmatter)
|
||||
# Use files_modified frontmatter if present, else extract from <files> elements
|
||||
if [ -n "$FILES_MODIFIED" ]; then
|
||||
PLAN_FILES["$plan_id"]="$FILES_MODIFIED"
|
||||
else
|
||||
FILES=$(grep -oP '(?<=<files>)[^<]+(?=</files>)' "$plan" | tr '\n' ',' | tr -d ' ')
|
||||
PLAN_FILES["$plan_id"]="${FILES%,}"
|
||||
fi
|
||||
|
||||
# Check for checkpoint tasks
|
||||
if grep -q 'type="checkpoint' "$plan"; then
|
||||
PLAN_CHECKPOINTS["$plan_id"]="true"
|
||||
else
|
||||
@@ -181,14 +166,13 @@ for plan in "${UNEXECUTED[@]}"; do
|
||||
done
|
||||
```
|
||||
|
||||
**Dependency detection priority:**
|
||||
**Dependency detection:**
|
||||
|
||||
1. **If `depends_on` frontmatter exists:** Use it directly
|
||||
2. **If `parallelizable: false` in frontmatter:** Mark as dependent (even without explicit depends_on)
|
||||
3. **If no frontmatter:** Fall back to inference:
|
||||
2. **If no frontmatter:** Fall back to inference:
|
||||
- Parse `requires` from old frontmatter format
|
||||
- Detect file conflicts via `<files>` elements
|
||||
- Check for SUMMARY references in @context
|
||||
3. **File conflicts:** Detected separately in step 4
|
||||
|
||||
**3. Build dependency graph:**
|
||||
|
||||
@@ -249,20 +233,15 @@ done
|
||||
- If Plan B reads file created by Plan A → B depends on A
|
||||
- If Plan B references Plan A's SUMMARY in @context → B depends on A
|
||||
|
||||
**5. Categorize plans (frontmatter-aware):**
|
||||
**5. Categorize plans:**
|
||||
|
||||
| Category | Criteria | Action |
|
||||
|----------|----------|--------|
|
||||
| independent | `parallelizable: true` in frontmatter OR (no frontmatter AND no inferred dependencies) | Can run in parallel (Wave 1) |
|
||||
| dependent | `parallelizable: false` OR has depends_on OR inferred dependencies | Wait for dependency |
|
||||
| independent | Empty `depends_on` AND no file conflicts | Can run in parallel (Wave 1) |
|
||||
| dependent | Has `depends_on` OR file conflicts with earlier plan | Wait for dependency |
|
||||
| has_checkpoints | Contains checkpoint tasks | Foreground or skip checkpoints |
|
||||
|
||||
**Categorization priority:**
|
||||
1. If `parallelizable` frontmatter exists: Use it directly
|
||||
2. If no frontmatter: Use inferred category from file/SUMMARY analysis
|
||||
3. `has_checkpoints` applies regardless of frontmatter
|
||||
|
||||
**6. Build execution waves (topological sort, frontmatter-aware):**
|
||||
**6. Build execution waves (topological sort):**
|
||||
|
||||
```bash
|
||||
# Calculate wave for each plan
|
||||
@@ -274,29 +253,8 @@ calculate_wave() {
|
||||
|
||||
local max_dep_wave=0
|
||||
|
||||
# Check for explicit parallelizable: false (force Wave 2+ even without deps)
|
||||
if [ "${PLAN_HAS_FRONTMATTER[$plan]}" = "true" ]; then
|
||||
if [ "${PLAN_PARALLELIZABLE[$plan]}" = "false" ] && [ -z "${PLAN_DEPENDS_ON[$plan]}" ]; then
|
||||
# parallelizable: false without deps = Wave 2 (wait for all Wave 1)
|
||||
max_dep_wave=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check frontmatter depends_on first
|
||||
if [ -n "${PLAN_DEPENDS_ON[$plan]}" ]; then
|
||||
IFS=',' read -ra deps <<< "${PLAN_DEPENDS_ON[$plan]}"
|
||||
for dep in "${deps[@]}"; do
|
||||
[ -z "$dep" ] && continue
|
||||
# Only consider deps in current phase (unexecuted)
|
||||
if [[ " ${!PLAN_FILES[*]} " =~ " $dep " ]]; then
|
||||
dep_wave=$(calculate_wave "$dep")
|
||||
[ "$dep_wave" -gt "$max_dep_wave" ] && max_dep_wave="$dep_wave"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Fall back to inferred requires if no frontmatter depends_on
|
||||
if [ "${PLAN_HAS_FRONTMATTER[$plan]}" != "true" ] && [ -n "${PLAN_REQUIRES[$plan]}" ]; then
|
||||
# Check depends_on (from frontmatter or inferred)
|
||||
if [ -n "${PLAN_REQUIRES[$plan]}" ]; then
|
||||
IFS=',' read -ra dep_array <<< "${PLAN_REQUIRES[$plan]}"
|
||||
for dep in "${dep_array[@]}"; do
|
||||
[ -z "$dep" ] && continue
|
||||
|
||||
@@ -69,8 +69,8 @@ cat .planning/config.json 2>/dev/null | jq '.parallelization'
|
||||
- If `parallelization.enabled && parallelization.plan_level`: Planning will optimize for independence
|
||||
- Group tasks by vertical slice (feature A, feature B) not workflow stage (setup → implement → test)
|
||||
- Avoid unnecessary inter-plan dependencies
|
||||
- Mark explicit file ownership per plan via files_exclusive
|
||||
- Set parallelizable: true when genuinely independent
|
||||
- Track files each plan modifies via `files_modified`
|
||||
- Keep `depends_on` empty when genuinely independent
|
||||
- If disabled: Planning proceeds with sequential assumptions (current behavior)
|
||||
|
||||
**If config.json missing:** Assume parallelization enabled (new projects get it by default).
|
||||
@@ -334,13 +334,13 @@ See ~/.claude/get-shit-done/references/checkpoints.md for checkpoint structure.
|
||||
- Shared infrastructure (all features need auth setup first)
|
||||
- Single-concern phases (all plans ARE vertical slices already)
|
||||
|
||||
4. **Mark plans with parallelization frontmatter:**
|
||||
4. **Set plan frontmatter for parallelization:**
|
||||
|
||||
For each plan, determine:
|
||||
- `parallelizable: true` if no file overlap with siblings AND no depends_on
|
||||
- `parallelizable: false` if has dependencies or file conflicts
|
||||
- `depends_on: [plan-ids]` explicit dependencies from analysis
|
||||
- `files_exclusive: [paths]` files only this plan touches
|
||||
- `depends_on: [plan-ids]` — explicit dependencies (empty if independent)
|
||||
- `files_modified: [paths]` — files this plan will modify
|
||||
|
||||
`/gsd:execute-phase` uses these to detect parallelization opportunities automatically.
|
||||
|
||||
**Output:** Task groupings optimized for independence, frontmatter values determined.
|
||||
</step>
|
||||
@@ -427,32 +427,27 @@ Use template from `~/.claude/get-shit-done/templates/phase-prompt.md`.
|
||||
**Multiple plans:** Write separate files ({phase}-01-PLAN.md, {phase}-02-PLAN.md, etc.)
|
||||
|
||||
Each plan follows template structure with:
|
||||
- Frontmatter (phase, plan, type, parallelizable, depends_on, files_exclusive, domain)
|
||||
- Frontmatter (phase, plan, type, depends_on, files_modified, domain)
|
||||
- Objective (plan-specific goal, purpose, output)
|
||||
- Execution context (execute-plan.md, summary template, checkpoints.md if needed)
|
||||
- Context (@references to PROJECT, ROADMAP, STATE, codebase docs, RESEARCH/DISCOVERY/CONTEXT if exist, prior summaries, source files, prior decisions, deferred issues, concerns)
|
||||
- Tasks (XML format with types)
|
||||
- Verification, Success criteria, Output specification
|
||||
|
||||
**Parallelization frontmatter (from parallelization_aware step):**
|
||||
**Plan frontmatter:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
phase: XX-name
|
||||
plan: NN
|
||||
type: execute
|
||||
parallelizable: [true if independent, false if has dependencies]
|
||||
depends_on: [plan IDs from analysis, or empty array]
|
||||
files_exclusive: [files only this plan modifies]
|
||||
depends_on: [plan IDs this plan requires, or empty array]
|
||||
files_modified: [files this plan will modify]
|
||||
domain: [optional]
|
||||
---
|
||||
```
|
||||
|
||||
**Rules for parallelizable field:**
|
||||
- `true`: No file overlap with sibling plans, no depends_on entries, no SUMMARY references needed
|
||||
- `false`: Has dependencies, shares files, or requires sequential execution
|
||||
|
||||
**If parallelization disabled in config:** Still include fields but set `parallelizable: false` for all plans.
|
||||
**Parallelization is automatic:** `/gsd:execute-phase` analyzes `depends_on` and `files_modified` to determine which plans can run in parallel. No explicit flag needed.
|
||||
|
||||
**Context section population from frontmatter analysis:**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user