Skip to content

Commit e4c7707

Browse files
jwaldripclaude
andcommitted
fix: session retrospective — branch ordering, team mode hats, workflow transitions, merge strategy
Six fixes from GigSmart Requester MCP retrospective sessions (c19eaafb, 67904c3a): - Reorder elaboration Phase 6 to create intent branch before writing artifacts - Add Phase 5.8 git strategy question (change_strategy, auto_merge) during elaboration - Embed per-unit hat instructions in teammate prompts instead of relying on hook injection - Skip hat section in subagent-context.sh during team mode to avoid wrong-hat injection - Replace hardcoded planner/builder/reviewer handlers with generic workflow-array-driven hat transitions so custom workflows (red-team, blue-team, etc.) are honored - Add merge step on unit completion using auto_merge/auto_squash config - Update mode descriptions with concrete UX language (file editing, approval, autonomy) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 72442a9 commit e4c7707

File tree

4 files changed

+318
-101
lines changed

4 files changed

+318
-101
lines changed

hooks/subagent-context.sh

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -182,35 +182,39 @@ if [ -d "$INTENT_DIR" ] && ls "$INTENT_DIR"/unit-*.md 1>/dev/null 2>&1; then
182182
fi
183183
fi
184184

185-
# Load role/hat instructions (builder/reviewer are orchestration roles)
186-
HAT_FILE=""
187-
if [ -f ".ai-dlc/hats/${HAT}.md" ]; then
188-
HAT_FILE=".ai-dlc/hats/${HAT}.md"
189-
elif [ -n "$CLAUDE_PLUGIN_ROOT" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/hats/${HAT}.md" ]; then
190-
HAT_FILE="${CLAUDE_PLUGIN_ROOT}/hats/${HAT}.md"
191-
fi
185+
# In team mode, hat instructions are embedded in teammate prompts by /construct
186+
# Skip here to avoid injecting the orchestrator's hat instead of the per-unit hat
187+
if [ -z "${CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:-}" ]; then
188+
# Load role/hat instructions (builder/reviewer are orchestration roles)
189+
HAT_FILE=""
190+
if [ -f ".ai-dlc/hats/${HAT}.md" ]; then
191+
HAT_FILE=".ai-dlc/hats/${HAT}.md"
192+
elif [ -n "$CLAUDE_PLUGIN_ROOT" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/hats/${HAT}.md" ]; then
193+
HAT_FILE="${CLAUDE_PLUGIN_ROOT}/hats/${HAT}.md"
194+
fi
192195

193-
echo "### Current Role: $HAT"
194-
echo ""
196+
echo "### Current Role: $HAT"
197+
echo ""
195198

196-
if [ -n "$HAT_FILE" ] && [ -f "$HAT_FILE" ]; then
197-
# Parse frontmatter
198-
NAME=$(han parse yaml name -r --default "" < "$HAT_FILE" 2>/dev/null || echo "")
199-
MODE=$(han parse yaml mode -r --default "" < "$HAT_FILE" 2>/dev/null || echo "")
199+
if [ -n "$HAT_FILE" ] && [ -f "$HAT_FILE" ]; then
200+
# Parse frontmatter
201+
NAME=$(han parse yaml name -r --default "" < "$HAT_FILE" 2>/dev/null || echo "")
202+
MODE=$(han parse yaml mode -r --default "" < "$HAT_FILE" 2>/dev/null || echo "")
200203

201-
# Get content after frontmatter
202-
INSTRUCTIONS=$(cat "$HAT_FILE" | sed '1,/^---$/d' | sed '1,/^---$/d')
204+
# Get content after frontmatter
205+
INSTRUCTIONS=$(cat "$HAT_FILE" | sed '1,/^---$/d' | sed '1,/^---$/d')
203206

204-
echo "**${NAME:-$HAT}** (Mode: ${MODE:-HITL})"
205-
echo ""
206-
if [ -n "$INSTRUCTIONS" ]; then
207-
echo "$INSTRUCTIONS"
207+
echo "**${NAME:-$HAT}** (Mode: ${MODE:-HITL})"
208+
echo ""
209+
if [ -n "$INSTRUCTIONS" ]; then
210+
echo "$INSTRUCTIONS"
211+
echo ""
212+
fi
213+
else
214+
# No hat file - role is an orchestrator that spawns discipline-specific agents
215+
echo "**$HAT** orchestrates work by spawning discipline-specific agents based on unit requirements."
208216
echo ""
209217
fi
210-
else
211-
# No hat file - role is an orchestrator that spawns discipline-specific agents
212-
echo "**$HAT** orchestrates work by spawning discipline-specific agents based on unit requirements."
213-
echo ""
214218
fi
215219

216220
# AI-DLC Workflow Rules (mandatory for all subagents)

skills/advance/SKILL.md

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const nextIndex = currentIndex + 1;
4545

4646
if (nextIndex >= workflow.length) {
4747
// At last hat - check DAG status to determine next action
48-
// See Step 2b below
48+
// See Steps 2b-2d below
4949
}
5050

5151
const nextHat = workflow[nextIndex];
@@ -68,7 +68,42 @@ CURRENT_UNIT=$(echo "$ITERATION_JSON" | han parse json currentUnit -r --default
6868
if [ -n "$CURRENT_UNIT" ] && [ -f "$INTENT_DIR/${CURRENT_UNIT}.md" ]; then
6969
update_unit_status "$INTENT_DIR/${CURRENT_UNIT}.md" "completed"
7070
fi
71+
```
72+
73+
### Step 2c: Merge Unit Branch on Completion
74+
75+
After marking a unit as completed, merge the unit branch into the intent branch:
76+
77+
```bash
78+
# Load config for merge settings
79+
source "${CLAUDE_PLUGIN_ROOT}/lib/config.sh"
80+
INTENT_DIR=".ai-dlc/${INTENT_SLUG}"
81+
CONFIG=$(get_ai_dlc_config "$INTENT_DIR")
82+
AUTO_MERGE=$(echo "$CONFIG" | jq -r '.auto_merge // "true"')
83+
AUTO_SQUASH=$(echo "$CONFIG" | jq -r '.auto_squash // "false"')
84+
85+
if [ "$AUTO_MERGE" = "true" ]; then
86+
UNIT_SLUG="${CURRENT_UNIT#unit-}"
87+
UNIT_BRANCH="ai-dlc/${INTENT_SLUG}/${UNIT_SLUG}"
88+
89+
# Ensure we're on the intent branch
90+
git checkout "ai-dlc/${INTENT_SLUG}"
91+
92+
# Merge unit branch
93+
if [ "$AUTO_SQUASH" = "true" ]; then
94+
git merge --squash "$UNIT_BRANCH"
95+
git commit -m "unit: ${CURRENT_UNIT} completed"
96+
else
97+
git merge --no-ff "$UNIT_BRANCH" -m "Merge ${CURRENT_UNIT} into intent branch"
98+
fi
99+
100+
# Clean up unit worktree
101+
WORKTREE_PATH="/tmp/ai-dlc-${INTENT_SLUG}-${UNIT_SLUG}"
102+
[ -d "$WORKTREE_PATH" ] && git worktree remove "$WORKTREE_PATH"
103+
fi
104+
```
71105

106+
```bash
72107
# Get DAG summary
73108
DAG_SUMMARY=$(get_dag_summary "$INTENT_DIR")
74109
ALL_COMPLETE=$(echo "$DAG_SUMMARY" | han parse json allComplete -r)
@@ -101,7 +136,7 @@ ${dagSummary.blockedUnits.join('\n')}
101136
Review blockers and unblock units to continue.`;
102137
```
103138

104-
### Step 2c: Spawn Newly Unblocked Units (Agent Teams)
139+
### Step 2d: Spawn Newly Unblocked Units (Agent Teams)
105140

106141
When `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` is enabled and completing a unit unblocks new units:
107142

@@ -158,10 +193,28 @@ When `/advance` completes the intent (all units done), output:
158193
### Criteria Satisfied
159194
{List of completion criteria}
160195
196+
### Merge to Default Branch
197+
198+
The intent branch is ready to merge:
199+
200+
```bash
201+
# Load merge config
202+
source "${CLAUDE_PLUGIN_ROOT}/lib/config.sh"
203+
INTENT_DIR=".ai-dlc/${INTENT_SLUG}"
204+
CONFIG=$(get_ai_dlc_config "$INTENT_DIR")
205+
DEFAULT_BRANCH=$(echo "$CONFIG" | jq -r '.default_branch')
206+
```
207+
208+
```
209+
Intent branch ready: ai-dlc/{intent-slug} → ${DEFAULT_BRANCH}
210+
211+
Create PR: gh pr create --base ${DEFAULT_BRANCH} --head ai-dlc/{intent-slug}
212+
```
213+
161214
### Next Steps
162215

163216
1. **Review changes** - Check the work on branch `ai-dlc/{intent-slug}`
164-
2. **Create PR** - `gh pr create --base main --head ai-dlc/{intent-slug}`
217+
2. **Create PR** - `gh pr create --base ${DEFAULT_BRANCH} --head ai-dlc/{intent-slug}`
165218
3. **Clean up worktrees** - `git worktree remove /tmp/ai-dlc-{intent-slug}`
166219
4. **Start new task** - Run `/reset` to clear state, then `/elaborate`
167220
```

skills/construct/SKILL.md

Lines changed: 145 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ Loop over ALL ready units from the DAG (not just one):
158158
```bash
159159
source "${CLAUDE_PLUGIN_ROOT}/lib/dag.sh"
160160
READY_UNITS=$(find_ready_units "$INTENT_DIR")
161+
162+
# Determine first construction hat (skip "elaborator" if present)
163+
WORKFLOW_HATS=$(echo "$STATE" | han parse json workflow)
164+
FIRST_HAT=$(echo "$WORKFLOW_HATS" | jq -r '[.[] | select(. != "elaborator")][0]')
161165
```
162166

163167
For EACH ready unit:
@@ -184,31 +188,59 @@ update_unit_status "$UNIT_FILE" "in_progress"
184188
3. **Initialize unit state in `unitStates`**:
185189

186190
```bash
187-
STATE=$(echo "$STATE" | han parse json --set "unitStates.${UNIT_NAME}.hat=planner" --set "unitStates.${UNIT_NAME}.retries=0")
191+
STATE=$(echo "$STATE" | han parse json --set "unitStates.${UNIT_NAME}.hat=${FIRST_HAT}" --set "unitStates.${UNIT_NAME}.retries=0")
188192
han keep save iteration.json "$STATE"
189193
```
190194

191-
4. **Create shared task via TaskCreate**:
195+
4. **Load hat instructions for the first hat**:
196+
197+
```bash
198+
# Load hat instructions for the teammate's role
199+
HAT_NAME="${FIRST_HAT}"
200+
HAT_FILE=""
201+
if [ -f ".ai-dlc/hats/${HAT_NAME}.md" ]; then
202+
HAT_FILE=".ai-dlc/hats/${HAT_NAME}.md"
203+
elif [ -n "$CLAUDE_PLUGIN_ROOT" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/hats/${HAT_NAME}.md" ]; then
204+
HAT_FILE="${CLAUDE_PLUGIN_ROOT}/hats/${HAT_NAME}.md"
205+
fi
206+
207+
# Extract instructions (content after second --- in frontmatter)
208+
HAT_INSTRUCTIONS=""
209+
if [ -n "$HAT_FILE" ]; then
210+
HAT_INSTRUCTIONS=$(sed '1,/^---$/d' "$HAT_FILE" | sed '1,/^---$/d')
211+
fi
212+
```
213+
214+
5. **Select agent type based on hat**:
215+
216+
- `planner` -> `Plan` agent
217+
- `builder` -> discipline-specific agent (see builder agent selection table below)
218+
- All other hats (`reviewer`, `red-team`, `blue-team`, etc.) -> `general-purpose` agent
219+
220+
6. **Create shared task via TaskCreate**:
192221

193222
```javascript
194223
TaskCreate({
195-
subject: `Plan: ${unitName}`,
196-
description: `Execute planner role for unit ${unitName}. Worktree: ${WORKTREE_PATH}`,
197-
activeForm: `Planning ${unitName}`
224+
subject: `${FIRST_HAT}: ${unitName}`,
225+
description: `Execute ${FIRST_HAT} role for unit ${unitName}. Worktree: ${WORKTREE_PATH}`,
226+
activeForm: `${FIRST_HAT}: ${unitName}`
198227
})
199228
```
200229

201-
5. **Spawn teammate**:
230+
7. **Spawn teammate with hat instructions in prompt**:
202231

203232
```javascript
204233
Task({
205-
subagent_type: "Plan", // planner hat starts as Plan agent
206-
description: `planner: ${unitName}`,
207-
name: `planner-${unitSlug}`,
234+
subagent_type: getAgentTypeForHat(FIRST_HAT, unit.discipline),
235+
description: `${FIRST_HAT}: ${unitName}`,
236+
name: `${FIRST_HAT}-${unitSlug}`,
208237
team_name: `ai-dlc-${intentSlug}`,
209238
mode: modeToAgentTeamsMode(intentMode),
210239
prompt: `
211-
Execute the planner role for this AI-DLC unit.
240+
Execute the ${FIRST_HAT} role for unit ${unitName}.
241+
242+
## Your Role: ${FIRST_HAT}
243+
${HAT_INSTRUCTIONS}
212244
213245
## CRITICAL: Work in Worktree
214246
**Worktree path:** ${WORKTREE_PATH}
@@ -241,53 +273,125 @@ Mode mapping:
241273

242274
The lead processes auto-delivered teammate messages. Handle each event type:
243275

244-
#### Planner Completes
276+
#### Teammate Completes (Any Hat)
245277

246-
1. Save the plan: `han keep save current-plan.md "<plan content>"`
247-
2. Update `unitStates.{unit}.hat = "builder"`
248-
3. Spawn builder teammate:
278+
When a teammate reports successful completion:
249279

250-
```javascript
251-
Task({
252-
subagent_type: getAgentForDiscipline(unit.discipline),
253-
description: `builder: ${unitName}`,
254-
name: `builder-${unitSlug}`,
255-
team_name: `ai-dlc-${intentSlug}`,
256-
mode: modeToAgentTeamsMode(intentMode),
257-
prompt: `Execute the builder role for unit ${unitName}...`
258-
})
280+
1. Read current hat for this unit from `unitStates.{unit}.hat`
281+
2. Find current hat's index in the `workflow` array
282+
3. Determine next hat: `workflow[currentIndex + 1]`
283+
284+
**If next hat exists** (not at end of workflow):
285+
286+
a. Update `unitStates.{unit}.hat = nextHat`
287+
b. Save state: `han keep save iteration.json "$STATE"`
288+
c. Load hat file for nextHat:
289+
290+
```bash
291+
HAT_NAME="${nextHat}"
292+
HAT_FILE=""
293+
if [ -f ".ai-dlc/hats/${HAT_NAME}.md" ]; then
294+
HAT_FILE=".ai-dlc/hats/${HAT_NAME}.md"
295+
elif [ -n "$CLAUDE_PLUGIN_ROOT" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/hats/${HAT_NAME}.md" ]; then
296+
HAT_FILE="${CLAUDE_PLUGIN_ROOT}/hats/${HAT_NAME}.md"
297+
fi
298+
HAT_INSTRUCTIONS=""
299+
if [ -n "$HAT_FILE" ]; then
300+
HAT_INSTRUCTIONS=$(sed '1,/^---$/d' "$HAT_FILE" | sed '1,/^---$/d')
301+
fi
259302
```
260303

261-
#### Builder Completes
304+
d. Select agent type based on hat:
305+
- `planner` -> `Plan` agent
306+
- `builder` -> discipline-specific agent (see builder agent selection table below)
307+
- All other hats (`reviewer`, `red-team`, `blue-team`, etc.) -> `general-purpose` agent
262308

263-
1. Update `unitStates.{unit}.hat = "reviewer"`
264-
2. Spawn reviewer teammate:
309+
e. Spawn teammate with hat instructions in prompt:
265310

266311
```javascript
267312
Task({
268-
subagent_type: "general-purpose",
269-
description: `reviewer: ${unitName}`,
270-
name: `reviewer-${unitSlug}`,
313+
subagent_type: getAgentTypeForHat(nextHat, unit.discipline),
314+
description: `${nextHat}: ${unitName}`,
315+
name: `${nextHat}-${unitSlug}`,
271316
team_name: `ai-dlc-${intentSlug}`,
272317
mode: modeToAgentTeamsMode(intentMode),
273-
prompt: `Execute the reviewer role for unit ${unitName}...`
318+
prompt: `
319+
Execute the ${nextHat} role for unit ${unitName}.
320+
321+
## Your Role: ${nextHat}
322+
${HAT_INSTRUCTIONS}
323+
324+
## CRITICAL: Work in Worktree
325+
**Worktree path:** ${WORKTREE_PATH}
326+
**Branch:** ${UNIT_BRANCH}
327+
328+
You MUST:
329+
1. cd ${WORKTREE_PATH}
330+
2. Verify you're on branch ${UNIT_BRANCH}
331+
3. Do ALL work in that directory
332+
4. Commit changes to that branch
333+
334+
## Unit: ${unitName}
335+
## Completion Criteria
336+
${unit.criteria}
337+
338+
Work according to your role. Report completion via SendMessage to the team lead when done.
339+
`
274340
})
275341
```
276342

277-
#### Reviewer APPROVES
343+
**If no next hat** (last hat in workflow -- unit complete):
344+
345+
a. Mark unit as completed: `update_unit_status "$UNIT_FILE" "completed"`
346+
b. Remove unit from `unitStates`
347+
c. Merge unit branch into intent branch:
348+
349+
```bash
350+
# Merge unit branch into intent branch
351+
source "${CLAUDE_PLUGIN_ROOT}/lib/config.sh"
352+
INTENT_DIR=".ai-dlc/${INTENT_SLUG}"
353+
CONFIG=$(get_ai_dlc_config "$INTENT_DIR")
354+
AUTO_MERGE=$(echo "$CONFIG" | jq -r '.auto_merge // "true"')
355+
AUTO_SQUASH=$(echo "$CONFIG" | jq -r '.auto_squash // "false"')
356+
357+
if [ "$AUTO_MERGE" = "true" ]; then
358+
UNIT_BRANCH="ai-dlc/${INTENT_SLUG}/${UNIT_SLUG}"
359+
git checkout "ai-dlc/${INTENT_SLUG}"
360+
361+
if [ "$AUTO_SQUASH" = "true" ]; then
362+
git merge --squash "$UNIT_BRANCH"
363+
git commit -m "unit: ${UNIT_NAME} completed"
364+
else
365+
git merge --no-ff "$UNIT_BRANCH" -m "Merge ${UNIT_NAME} into intent branch"
366+
fi
367+
368+
WORKTREE_PATH="/tmp/ai-dlc-${INTENT_SLUG}-${UNIT_SLUG}"
369+
[ -d "$WORKTREE_PATH" ] && git worktree remove "$WORKTREE_PATH"
370+
fi
371+
```
372+
373+
d. Check DAG for newly unblocked units
374+
e. For each newly ready unit, spawn at `workflow[0]` (first hat, skipping "elaborator" if present -- use the first non-elaborator hat):
375+
376+
```bash
377+
FIRST_HAT=$(echo "$WORKFLOW_HATS" | jq -r '[.[] | select(. != "elaborator")][0]')
378+
```
379+
380+
Then follow the same spawn logic from Step 3 (load hat instructions, select agent type, spawn teammate with hat instructions in prompt).
381+
382+
#### Teammate Reports Issues (Any Hat)
278383

279-
1. Mark unit completed: `update_unit_status "$UNIT_FILE" "completed"`
280-
2. Remove unit from `unitStates`
281-
3. Check DAG for newly unblocked units
282-
4. For each newly ready unit, go back to Step 3 logic (spawn at planner hat)
384+
When a teammate reports issues or rejects the work:
283385

284-
#### Reviewer REJECTS
386+
1. Read current hat index from `workflow` array
387+
2. Determine previous hat: `workflow[currentIndex - 1]`
388+
3. Increment `unitStates.{unit}.retries`
389+
4. If `retries >= 3`: Mark unit as blocked, document in `han keep save blockers.md`
390+
5. Otherwise: Set `unitStates.{unit}.hat = previousHat`
391+
6. Load hat file for previousHat
392+
7. Spawn teammate at previous hat with the feedback/issues in the prompt
285393

286-
1. Increment `unitStates.{unit}.retries`
287-
2. Check retry limit (max 3):
288-
- If `retries >= 3`: Mark unit as blocked, document in `han keep save blockers.md`
289-
- If `retries < 3`: Update `unitStates.{unit}.hat = "builder"`, spawn new builder teammate with reviewer feedback in prompt
290-
3. Continue monitoring
394+
This means ANY hat can reject -- not just the reviewer. A red-team finding issues sends work back to the previous hat.
291395

292396
#### Blocked
293397

0 commit comments

Comments
 (0)