From 755b28e644ef53d6b557b3cb0756010a2c8f2087 Mon Sep 17 00:00:00 2001 From: Lex Christopherson Date: Mon, 12 Jan 2026 14:55:40 -0600 Subject: [PATCH] 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 --- get-shit-done/templates/phase-prompt.md | 48 ++++-------- get-shit-done/workflows/execute-phase.md | 96 +++++++----------------- get-shit-done/workflows/plan-phase.md | 29 +++---- 3 files changed, 54 insertions(+), 119 deletions(-) diff --git a/get-shit-done/templates/phase-prompt.md b/get-shit-done/templates/phase-prompt.md index 99d1dda6d..bf02715d8 100644 --- a/get-shit-done/templates/phase-prompt.md +++ b/get-shit-done/templates/phase-prompt.md @@ -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 elements) +depends_on: [] # Plan IDs this plan requires (e.g., ["01-01"]). Empty = independent. +files_modified: [] # Files this plan modifies (from elements) domain: [optional - if domain skill loaded] --- - -**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 `` 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 - - [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` ``` -**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] --- @@ -323,21 +306,19 @@ Output: User model, API endpoints, and UI components. @.planning/PROJECT.md @.planning/ROADMAP.md -# No SUMMARY references - this plan is independent ... ``` -**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] --- @@ -350,14 +331,15 @@ Output: Stripe integration with user-linked payments. @.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 ... ``` -**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 diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md index d8c24a040..6154c5c5d 100644 --- a/get-shit-done/workflows/execute-phase.md +++ b/get-shit-done/workflows/execute-phase.md @@ -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 elements (all occurrences) - FILES=$(grep -oP '(?<=)[^<]+(?=)' "$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 elements + if [ -n "$FILES_MODIFIED" ]; then + PLAN_FILES["$plan_id"]="$FILES_MODIFIED" + else + FILES=$(grep -oP '(?<=)[^<]+(?=)' "$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 `` 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 diff --git a/get-shit-done/workflows/plan-phase.md b/get-shit-done/workflows/plan-phase.md index 00ba5d8d3..776be6e03 100644 --- a/get-shit-done/workflows/plan-phase.md +++ b/get-shit-done/workflows/plan-phase.md @@ -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. @@ -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:**