Merge pull request #298 from davesienkowski/feature/branching-strategy

Thanks @davesienkowski! Merging as-is, will follow up with squash merge option.
This commit is contained in:
TÂCHES
2026-01-30 17:23:42 -06:00
committed by GitHub
4 changed files with 295 additions and 2 deletions

View File

@@ -34,6 +34,7 @@ Parse current values (default to `true` if not present):
- `workflow.plan_check` — spawn plan checker during plan-phase
- `workflow.verifier` — spawn verifier during execute-phase
- `model_profile` — which model each agent uses (default: `balanced`)
- `git.branching_strategy` — branching approach (default: `"none"`)
## 3. Present Settings
@@ -77,6 +78,16 @@ AskUserQuestion([
{ label: "Yes", description: "Verify must-haves after execution" },
{ label: "No", description: "Skip post-execution verification" }
]
},
{
question: "Git branching strategy?",
header: "Branching",
multiSelect: false,
options: [
{ label: "None (Recommended)", description: "Commit directly to current branch" },
{ label: "Per Phase", description: "Create branch for each phase (gsd/phase-{N}-{name})" },
{ label: "Per Milestone", description: "Create branch for entire milestone (gsd/{version}-{name})" }
]
}
])
```
@@ -95,6 +106,9 @@ Merge new settings into existing config.json:
"research": true/false,
"plan_check": true/false,
"verifier": true/false
},
"git": {
"branching_strategy": "none" | "phase" | "milestone"
}
}
```
@@ -116,6 +130,7 @@ Display:
| Plan Researcher | {On/Off} |
| Plan Checker | {On/Off} |
| Execution Verifier | {On/Off} |
| Git Branching | {None/Per Phase/Per Milestone} |
These settings apply to future /gsd:plan-phase and /gsd:execute-phase runs.
@@ -130,7 +145,7 @@ Quick commands:
<success_criteria>
- [ ] Current config read
- [ ] User presented with 4 settings (profile + 3 toggles)
- [ ] Config updated with model_profile and workflow section
- [ ] User presented with 5 settings (profile + 3 workflow toggles + git branching)
- [ ] Config updated with model_profile, workflow, and git sections
- [ ] Changes confirmed to user
</success_criteria>

View File

@@ -7,6 +7,11 @@ Configuration options for `.planning/` directory behavior.
"planning": {
"commit_docs": true,
"search_gitignored": false
},
"git": {
"branching_strategy": "none",
"phase_branch_template": "gsd/phase-{phase}-{slug}",
"milestone_branch_template": "gsd/{milestone}-{slug}"
}
```
@@ -14,6 +19,9 @@ Configuration options for `.planning/` directory behavior.
|--------|---------|-------------|
| `commit_docs` | `true` | Whether to commit planning artifacts to git |
| `search_gitignored` | `false` | Add `--no-ignore` to broad rg searches |
| `git.branching_strategy` | `"none"` | Git branching approach: `"none"`, `"phase"`, or `"milestone"` |
| `git.phase_branch_template` | `"gsd/phase-{phase}-{slug}"` | Branch template for phase strategy |
| `git.milestone_branch_template` | `"gsd/{milestone}-{slug}"` | Branch template for milestone strategy |
</config_schema>
<commit_docs_behavior>
@@ -91,4 +99,80 @@ To use uncommitted mode:
</setup_uncommitted_mode>
<branching_strategy_behavior>
**Branching Strategies:**
| Strategy | When branch created | Branch scope | Merge point |
|----------|---------------------|--------------|-------------|
| `none` | Never | N/A | N/A |
| `phase` | At `execute-phase` start | Single phase | User merges after phase |
| `milestone` | At first `execute-phase` of milestone | Entire milestone | At `complete-milestone` |
**When `git.branching_strategy: "none"` (default):**
- All work commits to current branch
- Standard GSD behavior
**When `git.branching_strategy: "phase"`:**
- `execute-phase` creates/switches to a branch before execution
- Branch name from `phase_branch_template` (e.g., `gsd/phase-03-authentication`)
- All plan commits go to that branch
- User merges branches manually after phase completion
- `complete-milestone` offers to merge all phase branches
**When `git.branching_strategy: "milestone"`:**
- First `execute-phase` of milestone creates the milestone branch
- Branch name from `milestone_branch_template` (e.g., `gsd/v1.0-mvp`)
- All phases in milestone commit to same branch
- `complete-milestone` offers to merge milestone branch to main
**Template variables:**
| Variable | Available in | Description |
|----------|--------------|-------------|
| `{phase}` | phase_branch_template | Zero-padded phase number (e.g., "03") |
| `{slug}` | Both | Lowercase, hyphenated name |
| `{milestone}` | milestone_branch_template | Milestone version (e.g., "v1.0") |
**Checking the config:**
```bash
# Get branching strategy (default: none)
BRANCHING_STRATEGY=$(cat .planning/config.json 2>/dev/null | grep -o '"branching_strategy"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "none")
# Get phase branch template
PHASE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"phase_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/phase-{phase}-{slug}")
# Get milestone branch template
MILESTONE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"milestone_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/{milestone}-{slug}")
```
**Branch creation:**
```bash
# For phase strategy
if [ "$BRANCHING_STRATEGY" = "phase" ]; then
PHASE_SLUG=$(echo "$PHASE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
BRANCH_NAME=$(echo "$PHASE_BRANCH_TEMPLATE" | sed "s/{phase}/$PADDED_PHASE/g" | sed "s/{slug}/$PHASE_SLUG/g")
git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
fi
# For milestone strategy
if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
MILESTONE_SLUG=$(echo "$MILESTONE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
BRANCH_NAME=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed "s/{milestone}/$MILESTONE_VERSION/g" | sed "s/{slug}/$MILESTONE_SLUG/g")
git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
fi
```
**Use cases:**
| Strategy | Best for |
|----------|----------|
| `none` | Solo development, simple projects |
| `phase` | Code review per phase, granular rollback, team collaboration |
| `milestone` | Release branches, staging environments, PR per version |
</branching_strategy_behavior>
</planning_config>

View File

@@ -579,6 +579,125 @@ Progress: [updated progress bar]
</step>
<step name="handle_branches">
Check if branching was used and offer merge options.
**Check branching strategy:**
```bash
# Get branching strategy from config
BRANCHING_STRATEGY=$(cat .planning/config.json 2>/dev/null | grep -o '"branching_strategy"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "none")
```
**If strategy is "none":** Skip to git_tag step.
**For "phase" strategy — find phase branches:**
```bash
PHASE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"phase_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/phase-{phase}-{slug}")
# Extract prefix from template (before first variable)
BRANCH_PREFIX=$(echo "$PHASE_BRANCH_TEMPLATE" | sed 's/{.*//')
# Find all phase branches for this milestone
PHASE_BRANCHES=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ')
```
**For "milestone" strategy — find milestone branch:**
```bash
MILESTONE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"milestone_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/{milestone}-{slug}")
# Extract prefix from template
BRANCH_PREFIX=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed 's/{.*//')
# Find milestone branch
MILESTONE_BRANCH=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ' | head -1)
```
**If no branches found:** Skip to git_tag step.
**If branches exist — present merge options:**
```
## Git Branches Detected
Branching strategy: {phase/milestone}
Branches found:
{list of branches}
Options:
1. **Merge to main** — Merge branch(es) to main
2. **Delete without merging** — Branches already merged or not needed
3. **Keep branches** — Leave for manual handling
```
Use AskUserQuestion:
```
AskUserQuestion([
{
question: "How should branches be handled?",
header: "Branches",
multiSelect: false,
options: [
{ label: "Merge to main", description: "Merge all branches to main sequentially" },
{ label: "Delete without merging", description: "Branches already merged or not needed" },
{ label: "Keep branches", description: "Leave branches for manual handling later" }
]
}
])
```
**If "Merge to main":**
```bash
CURRENT_BRANCH=$(git branch --show-current)
git checkout main
# For phase strategy - merge each phase branch
if [ "$BRANCHING_STRATEGY" = "phase" ]; then
for branch in $PHASE_BRANCHES; do
echo "Merging $branch..."
git merge --no-ff "$branch" -m "Merge branch '$branch' for v[X.Y]"
done
fi
# For milestone strategy - merge milestone branch
if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
echo "Merging $MILESTONE_BRANCH..."
git merge --no-ff "$MILESTONE_BRANCH" -m "Merge branch '$MILESTONE_BRANCH' for v[X.Y]"
fi
git checkout "$CURRENT_BRANCH"
```
Report: "Merged branches to main"
**If "Delete without merging":**
```bash
if [ "$BRANCHING_STRATEGY" = "phase" ]; then
for branch in $PHASE_BRANCHES; do
git branch -d "$branch" 2>/dev/null || git branch -D "$branch"
done
fi
if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
git branch -d "$MILESTONE_BRANCH" 2>/dev/null || git branch -D "$MILESTONE_BRANCH"
fi
```
Report: "Deleted branches"
**If "Keep branches":**
Report: "Branches preserved for manual handling"
</step>
<step name="git_tag">
Create git tag for milestone:

View File

@@ -65,6 +65,81 @@ git check-ignore -q .planning 2>/dev/null && COMMIT_PLANNING_DOCS=false
```
Store `COMMIT_PLANNING_DOCS` for use in git operations.
**Load git branching config:**
```bash
# Get branching strategy (default: none)
BRANCHING_STRATEGY=$(cat .planning/config.json 2>/dev/null | grep -o '"branching_strategy"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "none")
# Get templates
PHASE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"phase_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/phase-{phase}-{slug}")
MILESTONE_BRANCH_TEMPLATE=$(cat .planning/config.json 2>/dev/null | grep -o '"milestone_branch_template"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/' || echo "gsd/{milestone}-{slug}")
```
Store `BRANCHING_STRATEGY` and templates for use in branch creation step.
</step>
<step name="handle_branching">
Create or switch to appropriate branch based on branching strategy.
**Skip if strategy is "none":**
```bash
if [ "$BRANCHING_STRATEGY" = "none" ]; then
# No branching, continue on current branch
exit 0
fi
```
**For "phase" strategy — create phase branch:**
```bash
if [ "$BRANCHING_STRATEGY" = "phase" ]; then
# Get phase name from directory (e.g., "03-authentication" → "authentication")
PHASE_NAME=$(basename "$PHASE_DIR" | sed 's/^[0-9]*-//')
# Create slug from phase name
PHASE_SLUG=$(echo "$PHASE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
# Apply template
BRANCH_NAME=$(echo "$PHASE_BRANCH_TEMPLATE" | sed "s/{phase}/$PADDED_PHASE/g" | sed "s/{slug}/$PHASE_SLUG/g")
# Create or switch to branch
git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
echo "Branch: $BRANCH_NAME (phase branching)"
fi
```
**For "milestone" strategy — create/switch to milestone branch:**
```bash
if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
# Get current milestone info from ROADMAP.md
MILESTONE_VERSION=$(grep -oE 'v[0-9]+\.[0-9]+' .planning/ROADMAP.md | head -1 || echo "v1.0")
MILESTONE_NAME=$(grep -A1 "## .*$MILESTONE_VERSION" .planning/ROADMAP.md | tail -1 | sed 's/.*- //' | cut -d'(' -f1 | tr -d ' ' || echo "milestone")
# Create slug
MILESTONE_SLUG=$(echo "$MILESTONE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
# Apply template
BRANCH_NAME=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed "s/{milestone}/$MILESTONE_VERSION/g" | sed "s/{slug}/$MILESTONE_SLUG/g")
# Create or switch to branch (same branch for all phases in milestone)
git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
echo "Branch: $BRANCH_NAME (milestone branching)"
fi
```
**Report branch status:**
```
Branching: {strategy} → {branch_name}
```
**Note:** All subsequent plan commits go to this branch. User handles merging based on their workflow.
</step>
<step name="validate_phase">