chore: initial monorepo scaffold + WDS Phase 1+2 artifacts
- Nx 22.7 monorepo (pnpm 11.1, TypeScript 5.9, Node 24) - apps/api: NestJS 11 (CJS conforme CODING-RULES.md PGD-DB-004) - apps/web: React 19 + Vite 8 (ESM) - libs/shared/api-interface: Zod contract base - Docker Compose dev: Postgres 18, Valkey 8, MinIO, Mailpit - WDS artifacts: - design-artifacts/A-Product-Brief/ (5 docs canônicos + 16 dialogs) - design-artifacts/B-Trigger-Map/ (hub + 4 personas + feature impact) - Stack canon: STACK.md v2.2 + CODING-RULES.md v2.0 + brand.md - AGENTS.md + README.md como entrada para devs/agentes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
139
.agents/skills/bmad-story-automator/steps-c/step-01-init.md
Normal file
139
.agents/skills/bmad-story-automator/steps-c/step-01-init.md
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
name: 'step-01-init'
|
||||
description: 'Check for existing state and route appropriately'
|
||||
nextStep: './step-02-preflight.md'
|
||||
continueStep: './step-01b-continue.md'
|
||||
outputFolder: '{output_folder}/story-automator'
|
||||
outputFile: '{outputFolder}/init-log-{timestamp}.md'
|
||||
rules: '../data/orchestrator-rules.md'
|
||||
scripts: '../scripts/story-automator'
|
||||
ensureStopHook: '../scripts/story-automator'
|
||||
stateHelper: '../scripts/story-automator'
|
||||
settingsFile: '{project-root}/.claude/settings.json'
|
||||
---
|
||||
|
||||
# Step 1: Initialize
|
||||
|
||||
**Goal:** Verify safeguards, check for existing state → resume or start fresh.
|
||||
|
||||
---
|
||||
|
||||
## Do
|
||||
|
||||
### 1. Verify Stop Hook Installation
|
||||
|
||||
**CRITICAL:** The Stop hook prevents premature stopping during orchestration.
|
||||
|
||||
Use script to ensure the Stop hook exists:
|
||||
```bash
|
||||
result=$("{ensureStopHook}" ensure-stop-hook --settings "{settingsFile}" \
|
||||
--command "{scripts} stop-hook" --timeout 10)
|
||||
ok=$(echo "$result" | jq -r '.ok')
|
||||
changed=$(echo "$result" | jq -r '.changed')
|
||||
verification_state=$(echo "$result" | jq -r '.verificationState // "verified"')
|
||||
message=$(echo "$result" | jq -r '.message // ""') # Helper returns provider-specific restart/setup guidance for Claude or Codex.
|
||||
```
|
||||
The settings path is used for Claude; Codex resolves `.codex/hooks.json` and `.codex/config.toml` from the project root.
|
||||
|
||||
**IF ok == false:** Report error and STOP.
|
||||
|
||||
**IF changed == true:**
|
||||
Display:
|
||||
```
|
||||
**Stop Hook Installed**
|
||||
|
||||
<message from helper>
|
||||
|
||||
This prevents the orchestrator from randomly stopping mid-workflow.
|
||||
|
||||
⚠️ **Please restart this active agent session** for the hook to take effect.
|
||||
|
||||
After restarting, run the story-automator workflow again.
|
||||
```
|
||||
**HALT** - Do not proceed until user restarts
|
||||
|
||||
**IF verification_state == "pending_trust":**
|
||||
Display:
|
||||
```
|
||||
**Stop Hook Pending Codex Trust**
|
||||
|
||||
<message from helper>
|
||||
|
||||
Trust this project in Codex, then restart Codex and run the story-automator workflow again.
|
||||
```
|
||||
**HALT** - Do not proceed until Codex can run the hook
|
||||
|
||||
**IF changed == false:**
|
||||
Display: "✓ Stop hook verified"
|
||||
Continue to step 2
|
||||
|
||||
### 2. Load Rules
|
||||
Load `{rules}` once. These apply to all subsequent steps.
|
||||
|
||||
### 3. Check for Existing State
|
||||
Search `{outputFolder}` for `orchestration-*.md` files.
|
||||
|
||||
Use deterministic state listing:
|
||||
```bash
|
||||
state_list=$("{stateHelper}" orchestrator-helper state-list "{outputFolder}")
|
||||
latest_incomplete=$(echo "$state_list" | jq -r '.files | map(select(.status == "COMPLETE" | not)) | sort_by(.lastUpdated) | last | .path // empty')
|
||||
```
|
||||
|
||||
**IF latest_incomplete is non-empty:**
|
||||
- Display: "**Found existing orchestration in progress.**"
|
||||
- Show: epic name, current story, current step, last updated
|
||||
- → Load `{continueStep}`
|
||||
- **STOP** (don't continue below)
|
||||
|
||||
**IF none found:**
|
||||
- Continue to step 4
|
||||
|
||||
### 4. Welcome
|
||||
Display:
|
||||
```
|
||||
**Welcome to Story Automator.**
|
||||
|
||||
I'll automate story implementation by spawning isolated sessions,
|
||||
handling code review loops, and committing completed stories.
|
||||
|
||||
Everything is logged for full resumability.
|
||||
```
|
||||
|
||||
### 5. Check Sprint Status (MANDATORY)
|
||||
```bash
|
||||
has_status=$("{stateHelper}" orchestrator-helper sprint-status exists)
|
||||
sprint_ok=$(echo "$has_status" | jq -r '.exists')
|
||||
```
|
||||
|
||||
**IF sprint_ok == false:** ABORT immediately.
|
||||
|
||||
Display:
|
||||
```
|
||||
**❌ Sprint status file not found.**
|
||||
|
||||
Expected: `_bmad-output/implementation-artifacts/sprint-status.yaml`
|
||||
|
||||
This file is required before running the story automator.
|
||||
Please run the **sprint-planning** workflow first to generate it.
|
||||
```
|
||||
**HALT** - Do not proceed.
|
||||
|
||||
**IF sprint_ok == true:**
|
||||
- Store for later reference during preflight
|
||||
- Will be used to check if earlier stories need completion
|
||||
|
||||
### 6. Setup
|
||||
Ensure `{outputFolder}` exists.
|
||||
|
||||
Append an initialization entry to `{outputFile}`:
|
||||
```bash
|
||||
printf \"[%s] init: stop-hook=%s existing_state=%s\\n\" \
|
||||
\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" \"${changed}\" \"${latest_incomplete}\" >> \"{outputFile}\"
|
||||
```
|
||||
|
||||
**Note:** Marker file path is resolved by `orchestrator-helper marker path` in step-02b-preflight-finalize after epic/story context is established.
|
||||
|
||||
---
|
||||
|
||||
## Then
|
||||
→ Load `{nextStep}`
|
||||
200
.agents/skills/bmad-story-automator/steps-c/step-01b-continue.md
Normal file
200
.agents/skills/bmad-story-automator/steps-c/step-01b-continue.md
Normal file
@@ -0,0 +1,200 @@
|
||||
---
|
||||
name: 'step-01b-continue'
|
||||
description: 'Handle workflow continuation from previous session'
|
||||
outputFolder: '{output_folder}/story-automator'
|
||||
outputFile: '{outputFolder}/orchestration-{epic_id}-{timestamp}.md'
|
||||
preflightStep: './step-02-preflight.md'
|
||||
preflightConfigStep: './step-02a-preflight-config.md'
|
||||
preflightFinalizeStep: './step-02b-preflight-finalize.md'
|
||||
executeStep: './step-03-execute.md'
|
||||
executeReviewStep: './step-03a-execute-review.md'
|
||||
executeFinishStep: './step-03b-execute-finish.md'
|
||||
executeCompleteStep: './step-03c-execute-complete.md'
|
||||
wrapupStep: './step-04-wrapup.md'
|
||||
stateFilePattern: '{outputFolder}/orchestration-*.md'
|
||||
stateHelper: '../scripts/story-automator'
|
||||
ensureMarkerGitignore: '../scripts/story-automator'
|
||||
deriveProjectSlug: '../scripts/story-automator'
|
||||
listSessions: '../scripts/story-automator'
|
||||
sprintCompare: '../scripts/story-automator'
|
||||
tmuxCommands: '../data/tmux-commands.md'
|
||||
# Optional: provided by workflow.md when using Resume mode (skips state search)
|
||||
resumeStatePath: ''
|
||||
---
|
||||
|
||||
# Step 1b: Continue Previous Session
|
||||
|
||||
**Goal:** Load existing state and let user choose how to proceed.
|
||||
|
||||
---
|
||||
|
||||
## Do
|
||||
|
||||
### 1. Load State Document
|
||||
|
||||
**IF `{resumeStatePath}` is provided (from workflow.md Resume routing):**
|
||||
Use it directly: `state_file="{resumeStatePath}"`
|
||||
|
||||
**ELSE (called from step-01-init or no path provided):**
|
||||
Find the most recent incomplete state document using `{stateFilePattern}`:
|
||||
```bash
|
||||
result=$("{stateHelper}" orchestrator-helper state-latest-incomplete "{outputFolder}")
|
||||
state_file=$(echo "$result" | jq -r '.path // empty')
|
||||
```
|
||||
|
||||
**IF state_file is empty:** Display "No incomplete orchestration found." and HALT.
|
||||
|
||||
**Then extract from state_file:**
|
||||
- `epic`, `epicName`, `storyRange`
|
||||
- `currentStep`, `status`
|
||||
- `stepsCompleted`, `storiesCompleted`
|
||||
- Last action from action log
|
||||
|
||||
Use deterministic summary:
|
||||
```bash
|
||||
summary=$("{stateHelper}" orchestrator-helper state-summary "$state_file")
|
||||
```
|
||||
|
||||
### 2. Verify Against Sprint Status
|
||||
Load `_bmad-output/implementation-artifacts/sprint-status.yaml`.
|
||||
|
||||
**Compare with state document (run in parallel with session inventory):**
|
||||
- Check if earlier stories (before `currentStory`) are marked `done` in sprint-status
|
||||
- If any earlier stories are NOT `done`:
|
||||
```
|
||||
**Warning:** Stories {X, Y} are not complete in sprint-status.yaml.
|
||||
|
||||
[B]atch them first - Add to queue before continuing
|
||||
[S]kip - Continue from current story anyway
|
||||
```
|
||||
**Wait.**
|
||||
- If B: Add incomplete stories to beginning of queue
|
||||
- If S: Note skip in action log, continue
|
||||
|
||||
Use deterministic parallel baseline:
|
||||
```bash
|
||||
tmp_compare=$(mktemp)
|
||||
tmp_sessions=$(mktemp)
|
||||
|
||||
("{sprintCompare}" sprint-compare --state "$state_file" --sprint "_bmad-output/implementation-artifacts/sprint-status.yaml" > "$tmp_compare") &
|
||||
compare_pid=$!
|
||||
|
||||
project_slug=$(echo "$("{deriveProjectSlug}" derive-project-slug --project-root "{project-root}")" | jq -r '.slug')
|
||||
("{listSessions}" list-sessions --slug "$project_slug" > "$tmp_sessions") &
|
||||
sessions_pid=$!
|
||||
|
||||
wait "$compare_pid"
|
||||
wait "$sessions_pid"
|
||||
|
||||
compare=$(cat "$tmp_compare")
|
||||
sessions=$(cat "$tmp_sessions")
|
||||
rm -f "$tmp_compare" "$tmp_sessions"
|
||||
|
||||
incomplete=$(echo "$compare" | jq -r '.incomplete | join(", ")')
|
||||
session_count=$(echo "$sessions" | jq -r '.count')
|
||||
```
|
||||
|
||||
### 3. Check Active Sessions
|
||||
Using `{tmuxCommands}`, check for existing T-Mux sessions for THIS PROJECT ONLY.
|
||||
|
||||
**Generate project slug first:**
|
||||
```bash
|
||||
project_slug=$(echo "$("{deriveProjectSlug}" derive-project-slug --project-root "{project-root}")" | jq -r '.slug')
|
||||
```
|
||||
|
||||
**Then list sessions matching:** `sa-{project_slug}-*`
|
||||
|
||||
This ensures we only see sessions spawned by THIS project's story-automator, not sessions from other projects.
|
||||
|
||||
Use `sessions` and `session_count` from step 2 parallel baseline.
|
||||
|
||||
### 4. Present Status
|
||||
```
|
||||
**Resuming: {epicName}**
|
||||
|
||||
Status: {status}
|
||||
Progress: {storiesCompleted}/{totalStories} stories
|
||||
Current: Story {N}, Step: {currentStep}
|
||||
Last action: {lastAction}
|
||||
|
||||
Active sessions: {count or 'None'}
|
||||
```
|
||||
|
||||
### 5. Present Options
|
||||
```
|
||||
[R]esume - Continue from where you left off
|
||||
[V]iew - See action log details
|
||||
[M]odify - Change overrides or context
|
||||
[S]tart Over - Restart this epic (keeps backup)
|
||||
[X]Abort - Cancel orchestration
|
||||
```
|
||||
|
||||
**Wait for user input.**
|
||||
|
||||
#### Menu Handling Logic:
|
||||
- IF R: Create marker file, then route based on `status` and `currentStep`:
|
||||
- READY → `{preflightFinalizeStep}`
|
||||
- INITIALIZING → `{preflightConfigStep}`
|
||||
- IN_PROGRESS / PAUSED → route by `currentStep`:
|
||||
- `step-03-execute` or `create` or `dev` → `{executeStep}`
|
||||
- `step-03a-execute-review` or `auto` or `review` → `{executeReviewStep}`
|
||||
- `step-03b-execute-finish` or `commit` or `retro` → `{executeFinishStep}`
|
||||
- `step-03c-execute-complete` → `{executeCompleteStep}`
|
||||
- (default) → `{executeStep}`
|
||||
- EXECUTION_COMPLETE → `{wrapupStep}`
|
||||
- COMPLETE → `{wrapupStep}`
|
||||
- ABORTED → display warning and redisplay this menu
|
||||
- IF V: Show last 20 action log entries, then redisplay this menu
|
||||
- IF M: Allow override changes, save, then redisplay this menu
|
||||
- IF S: Rename state to `.backup-{timestamp}` then load `{preflightStep}` (new state will be created at `{outputFile}`)
|
||||
- IF X: Set status="ABORTED", display confirmation, end workflow
|
||||
- IF Any other: help user respond, then redisplay this menu
|
||||
|
||||
#### EXECUTION RULES:
|
||||
- ALWAYS halt and wait for user input after presenting menu
|
||||
- ONLY route to a step after handling the selected option
|
||||
- After non-routing options, return to this menu
|
||||
- Keep prompts concise; if user is unsure, ask one clarifying question before redisplaying options
|
||||
|
||||
### 6. Handle Choice
|
||||
|
||||
| Choice | Action |
|
||||
|--------|--------|
|
||||
| **R** | **First:** Create marker file (see below), **then** route based on `status` |
|
||||
| **V** | Show last 20 action log entries → redisplay options |
|
||||
| **M** | Allow override changes, save → redisplay options |
|
||||
| **S** | Rename state to `.backup-{timestamp}` → `{preflightStep}` |
|
||||
| **X** | Set status="ABORTED", display confirmation, end workflow |
|
||||
|
||||
#### On [R]esume: Create Marker File BEFORE Routing
|
||||
|
||||
**CRITICAL:** Only create marker file when user confirms resume. This prevents stop hook from firing during menu wait.
|
||||
|
||||
Create the active runtime marker with orchestration context:
|
||||
```json
|
||||
{
|
||||
"epic": "{epic}",
|
||||
"currentStory": "{currentStory}",
|
||||
"storiesRemaining": {remaining_count},
|
||||
"stateFile": "{state_document_path}",
|
||||
"startedAt": "{timestamp}"
|
||||
}
|
||||
```
|
||||
|
||||
Use deterministic marker creation:
|
||||
```bash
|
||||
marker_info=$("{stateHelper}" orchestrator-helper marker path)
|
||||
marker_entry=$(echo "$marker_info" | jq -r '.entry')
|
||||
"{ensureMarkerGitignore}" ensure-marker-gitignore --gitignore ".gitignore" --entry "$marker_entry"
|
||||
|
||||
"{stateHelper}" orchestrator-helper marker create --epic "{epic}" --story "{currentStory}" \
|
||||
--remaining {remaining_count} --state-file "{state_document_path}" \
|
||||
--project-slug "$project_slug" --pid "$$" --heartbeat "{timestamp}"
|
||||
```
|
||||
|
||||
**Then** route per Menu Handling Logic in section 5 above.
|
||||
|
||||
---
|
||||
|
||||
## Then
|
||||
→ Load appropriate step based on choice
|
||||
200
.agents/skills/bmad-story-automator/steps-c/step-02-preflight.md
Normal file
200
.agents/skills/bmad-story-automator/steps-c/step-02-preflight.md
Normal file
@@ -0,0 +1,200 @@
|
||||
---
|
||||
name: 'step-02-preflight'
|
||||
description: 'Gather epic, story selection, and complexity analysis'
|
||||
nextStep: './step-02a-preflight-config.md'
|
||||
outputFolder: '{output_folder}/story-automator'
|
||||
outputFile: '{outputFolder}/preflight-{epic_id}-{timestamp}.md'
|
||||
parseEpic: '../scripts/story-automator'
|
||||
parseStoryRange: '../scripts/story-automator'
|
||||
parseStory: '../scripts/story-automator'
|
||||
stateHelper: '../scripts/story-automator'
|
||||
defaultEpicPath: '{output_folder}/planning-artifacts/epics.md'
|
||||
defaultSprintStatusFile: '{output_folder}/implementation-artifacts/sprint-status.yaml'
|
||||
complexityRules: '../data/complexity-rules.json'
|
||||
complexityScoring: '../data/complexity-scoring.md'
|
||||
preflightRequirements: '../data/preflight-requirements.md'
|
||||
---
|
||||
# Step 2: Pre-flight (Epic + Complexity)
|
||||
|
||||
**Goal:** Gather epic, story range, complexity analysis, and custom instructions.
|
||||
**Interaction mode:** Collaborative discovery and clarification.
|
||||
|
||||
---
|
||||
|
||||
## 🚨 BEFORE STARTING: Load Requirements
|
||||
|
||||
**CRITICAL:** Load and read `{preflightRequirements}` FIRST. It contains MANDATORY sequence rules, FORBIDDEN patterns, and verification gates that MUST be followed.
|
||||
|
||||
---
|
||||
|
||||
## Do
|
||||
|
||||
### 1. Confirm Epic File
|
||||
```
|
||||
**Epic source**
|
||||
|
||||
Default epic file: `{defaultEpicPath}`
|
||||
Use this file? [Y/n]
|
||||
```
|
||||
|
||||
If user confirms (Y/Enter), set `epic_path="{defaultEpicPath}"`.
|
||||
If user says no, ask for epic file path and set `epic_path` from response.
|
||||
If confirmed default does not exist, tell user and request explicit path.
|
||||
|
||||
**Wait.**
|
||||
|
||||
### 2. Review Epic
|
||||
Parse epic file deterministically:
|
||||
```bash
|
||||
epic_json=$("{parseEpic}" parse-epic --file "{epic_path}")
|
||||
epic_name=$(echo "$epic_json" | jq -r '.epicTitle')
|
||||
story_count=$(echo "$epic_json" | jq -r '.count')
|
||||
story_titles=$(echo "$epic_json" | jq -r '.stories[] | "\(.storyId) \(.title)"')
|
||||
story_ids_csv=$(echo "$epic_json" | jq -r '.stories[] | .storyId' | paste -sd, -)
|
||||
sprint_exists=$("{stateHelper}" orchestrator-helper sprint-status exists)
|
||||
story_status_rows="(sprint-status unavailable at {defaultSprintStatusFile})"
|
||||
if [ "$sprint_exists" = "true" ]; then
|
||||
story_status_rows=$(echo "$epic_json" | jq -r '.stories[] | .storyId' | while read -r sid; do
|
||||
status_json=$("{stateHelper}" orchestrator-helper sprint-status get "$sid")
|
||||
st=$(echo "$status_json" | jq -r '.status // "unknown"')
|
||||
printf -- "- %s | %s\n" "$sid" "$st"
|
||||
done)
|
||||
fi
|
||||
```
|
||||
|
||||
Display:
|
||||
```
|
||||
**Epic:** {epic_name}
|
||||
|
||||
Stories found:
|
||||
1. {storyId} {title}
|
||||
2. {storyId} {title}
|
||||
...
|
||||
|
||||
Total: {story_count}
|
||||
|
||||
Current sprint-status ({defaultSprintStatusFile}):
|
||||
{story_status_rows}
|
||||
|
||||
Which stories? (e.g., `1-3`, `all`, `1,3,5`)
|
||||
```
|
||||
If user hesitates, suggest `all` as default and confirm.
|
||||
|
||||
**Wait.**
|
||||
|
||||
### 3. Read Stories and Compute Complexity (MANDATORY - DO NOT SKIP)
|
||||
|
||||
> **🚨 CRITICAL:** This step MUST use the Python helper for complexity scoring. NEVER manually assess complexity by reading story content.
|
||||
|
||||
For each story in range, extract complexity **programmatically**:
|
||||
|
||||
**3a. Parse story range:**
|
||||
```bash
|
||||
range_json=$("{parseStoryRange}" parse-story-range --input "{user_selection}" --total "$story_count" --ids "$story_ids_csv")
|
||||
selected_ids=$(echo "$range_json" | jq -r '.storyIds[]')
|
||||
selected_count=$(echo "$range_json" | jq -r '.count')
|
||||
first_story_id=$(echo "$range_json" | jq -r '.storyIds[0]')
|
||||
epic_id=$(echo "$first_story_id" | cut -d. -f1)
|
||||
```
|
||||
|
||||
**3b. Get complexity for EACH story using Python helper:**
|
||||
```bash
|
||||
# Initialize accumulator - REQUIRED
|
||||
stories_json='[]'
|
||||
|
||||
# For EACH story_id in selected_ids, run:
|
||||
story_json=$("{parseStory}" parse-story --epic "{epic_path}" --story "$story_id" --rules "{complexityRules}")
|
||||
|
||||
# Extract and accumulate - REQUIRED
|
||||
story_title=$(echo "$story_json" | jq -r '.title')
|
||||
story_level=$(echo "$story_json" | jq -r '.complexity.level')
|
||||
story_score=$(echo "$story_json" | jq -r '.complexity.score')
|
||||
story_reasons=$(echo "$story_json" | jq -r '.complexity.reasons // []')
|
||||
stories_json=$(echo "$stories_json" | jq -c --arg id "$story_id" --arg title "$story_title" --arg level "$story_level" --argjson score "$story_score" --argjson reasons "$story_reasons" \
|
||||
'. + [{storyId:$id,title:$title,complexity:{level:$level,score:$score,reasons:$reasons}}]')
|
||||
```
|
||||
|
||||
Refer to `{complexityScoring}` for scoring criteria and thresholds.
|
||||
|
||||
**Parallelism Policy (MANDATORY):**
|
||||
|
||||
- If `selected_count >= 4`: run per-story complexity parsing in parallel subprocesses (max 4 workers).
|
||||
- If `selected_count < 4`: run sequentially.
|
||||
- In both modes, return only summary fields to parent context: `storyId`, `title`, `complexity.level`, `complexity.score`, `complexity.reasons`.
|
||||
|
||||
```bash
|
||||
# Deterministic threshold
|
||||
if [ "$selected_count" -ge 4 ]; then
|
||||
# Parallel mode (max 4 workers)
|
||||
tmp_story_complexity=$(mktemp)
|
||||
printf "%s\n" $selected_ids | xargs -I{} -P 4 sh -c '
|
||||
"{parseStory}" parse-story --epic "{epic_path}" --story "{}" --rules "{complexityRules}" \
|
||||
| jq -c "{storyId:.storyId,title:.title,complexity:.complexity}"
|
||||
' > "$tmp_story_complexity"
|
||||
stories_json=$(jq -s '.' "$tmp_story_complexity")
|
||||
rm -f "$tmp_story_complexity"
|
||||
else
|
||||
# Sequential mode
|
||||
stories_json='[]'
|
||||
for story_id in $selected_ids; do
|
||||
story_json=$("{parseStory}" parse-story --epic "{epic_path}" --story "$story_id" --rules "{complexityRules}")
|
||||
stories_json=$(echo "$stories_json" | jq -c --argjson s "$(echo "$story_json" | jq -c '{storyId:.storyId,title:.title,complexity:.complexity}')" '. + [$s]')
|
||||
done
|
||||
fi
|
||||
```
|
||||
|
||||
**3c. Display Complexity Matrix (REQUIRED):**
|
||||
|
||||
Display the Complexity Matrix using the template from `{preflightRequirements}`.
|
||||
|
||||
**3d. VERIFICATION GATE:**
|
||||
|
||||
Follow the verification gate from `{preflightRequirements}` before proceeding.
|
||||
|
||||
---
|
||||
|
||||
### 4. Custom Instructions
|
||||
```
|
||||
**Any custom instructions?**
|
||||
|
||||
Examples:
|
||||
- "Always run tests after changes"
|
||||
- "Prioritize stories 3 and 5"
|
||||
- "Be extra careful with database migrations"
|
||||
- "Use strict typing throughout"
|
||||
|
||||
Enter instructions or 'none':
|
||||
```
|
||||
If user is unsure, recommend `none` and continue.
|
||||
|
||||
**Wait.**
|
||||
|
||||
Store response as `custom_instructions` (use "" for none).
|
||||
|
||||
### 5. Proceed to Configuration
|
||||
|
||||
Persist preflight snapshot before continuing:
|
||||
```bash
|
||||
mkdir -p "{outputFolder}"
|
||||
cat > "{outputFile}" <<EOF
|
||||
# Preflight Snapshot
|
||||
|
||||
- Timestamp: {timestamp}
|
||||
- Epic path: {epic_path}
|
||||
- Epic name: {epic_name}
|
||||
- Story count: {story_count}
|
||||
- Selected count: {selected_count}
|
||||
- Selected IDs: {selected_ids}
|
||||
- Custom instructions: {custom_instructions}
|
||||
|
||||
## Complexity Summary
|
||||
$(echo "$stories_json" | jq -r '.[] | "- \(.storyId) | \(.complexity.level) | score=\(.complexity.score)"')
|
||||
EOF
|
||||
```
|
||||
|
||||
Carry forward: `epic_path`, `epic_name`, `story_count`, `story_ids_csv`, `range_json`, `selected_ids`, `selected_count`, `stories_json`, `epic_id`, `first_story_id`, `custom_instructions`.
|
||||
|
||||
---
|
||||
|
||||
## Then
|
||||
→ Load and execute `{nextStep}`
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
name: 'step-02a-preflight-config'
|
||||
description: 'Configure agents and execution settings, then create state document'
|
||||
nextStep: './step-02b-preflight-finalize.md'
|
||||
stateTemplate: '../templates/state-document.md'
|
||||
outputFolder: '{output_folder}/story-automator'
|
||||
outputFile: '{outputFolder}/orchestration-{epic_id}-{timestamp}.md'
|
||||
buildStateDoc: '../scripts/story-automator'
|
||||
agentConfigPrompts: '../data/agent-config-prompts.md'
|
||||
agentConfigPresets: '../data/agent-config-presets.json'
|
||||
---
|
||||
# Step 2a: Pre-flight Configuration
|
||||
|
||||
**Goal:** Configure agents and execution settings, then create the orchestration state document.
|
||||
**Interaction mode:** Guided configuration (collaborative inputs, deterministic state creation).
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Step 2 completed.
|
||||
- Variables available: `epic_id`, `epic_name`, `range_json`, `stories_json`, `selected_count`, `custom_instructions`.
|
||||
|
||||
---
|
||||
|
||||
## Do
|
||||
|
||||
### 1. Configure Execution Preferences
|
||||
|
||||
> **PREREQUISITE:** Step 2 (preflight) MUST be complete. The Complexity Matrix MUST have been displayed. If not, STOP and complete step 2 first.
|
||||
|
||||
```
|
||||
**Execution Settings:**
|
||||
|
||||
1. **Skip the 'automate' step (test automation)?** [N]o (default) / [Y]es
|
||||
2. **Max parallel sessions?** (tmux sessions running concurrently, default: 1)
|
||||
|
||||
Enter choices (e.g., `N 1` or `Y 3`):
|
||||
```
|
||||
|
||||
**Wait.**
|
||||
|
||||
Store responses as `skip_automate` (true/false) and `max_parallel` (integer).
|
||||
|
||||
### 2. Configure Agent (Complexity-Aware)
|
||||
|
||||
Using the complexity data from `stories_json`, present agent configuration options that reference the actual complexity breakdown.
|
||||
|
||||
**2a. Check for Saved Presets**
|
||||
|
||||
```bash
|
||||
presets_result=$("{buildStateDoc}" agent-config list --file "{agentConfigPresets}")
|
||||
preset_count=$(echo "$presets_result" | jq -r '.count')
|
||||
```
|
||||
|
||||
Store `preset_count` — this determines whether [L]oad option appears in the menu.
|
||||
|
||||
**2b. Present Complexity-Based Agent Options**
|
||||
|
||||
Display prompts from `{agentConfigPrompts}`, selecting the appropriate table variant:
|
||||
- If `skip_automate` is false: show table WITH `auto` column
|
||||
- If `skip_automate` is true: show table WITHOUT `auto` column
|
||||
- If `preset_count > 0`: include [L]oad saved option
|
||||
- If `preset_count == 0`: omit [L] option
|
||||
|
||||
**Wait.**
|
||||
|
||||
**2c. Handle Selection**
|
||||
|
||||
- **IF S:** Build `agent_config_json` from defaults (no save prompt).
|
||||
- **IF U or C:** Follow Uniform/Custom prompts from `{agentConfigPrompts}`, build `agent_config_json`, then proceed to **2d (Save Prompt)**.
|
||||
- **IF L:** Follow Load Saved Preset prompt from `{agentConfigPrompts}`. Load preset config as `agent_config_json` (no save prompt).
|
||||
|
||||
```bash
|
||||
# Example shape with complexity-based config (auto column included when not skipped)
|
||||
agent_config_json='{
|
||||
"complexityBased": true,
|
||||
"low": {"create":{"primary":"...","fallback":"..."},"dev":{...},"auto":{...},"review":{...}},
|
||||
"medium": {"create":{...},"dev":{...},"auto":{...},"review":{...}},
|
||||
"high": {"create":{...},"dev":{...},"auto":{...},"review":{...}},
|
||||
"auto": {"skip": $skip_automate}
|
||||
}'
|
||||
```
|
||||
|
||||
Store:
|
||||
- `agent_config_json` = full config object
|
||||
- `primary_agent` = default primary (for backwards compatibility)
|
||||
|
||||
**2d. Save Prompt (U/C only)**
|
||||
|
||||
Only when user chose **[U]niform** or **[C]ustom**, follow the Save Configuration prompt from `{agentConfigPrompts}`:
|
||||
|
||||
```bash
|
||||
# If user provides a name:
|
||||
"{buildStateDoc}" agent-config save --file "{agentConfigPresets}" --name "$save_name" --config-json "$agent_config_json"
|
||||
```
|
||||
|
||||
### 3. Review
|
||||
|
||||
Display configuration summary:
|
||||
- Epic and story range
|
||||
- Custom instructions (if any)
|
||||
- Agent configuration
|
||||
- Execution settings
|
||||
|
||||
Pause for confirmation before starting execution.
|
||||
|
||||
### 3b. Confirm Autonomous Start (Optional Checkpoint)
|
||||
|
||||
Before creating state and launching autonomous phases, confirm:
|
||||
```
|
||||
Proceed with autonomous execution after preflight? [Y/n]
|
||||
```
|
||||
|
||||
**Wait.**
|
||||
|
||||
- If `Y`/Enter: continue.
|
||||
- If `n`: return to Step 1 (settings) for adjustments.
|
||||
|
||||
### 4. Create State Document
|
||||
|
||||
From `{stateTemplate}`:
|
||||
- Generate: `orchestration-{epic_id}-{timestamp}.md`
|
||||
- Fill frontmatter with all config
|
||||
- Initialize story progress table
|
||||
- Set status: "READY"
|
||||
- Save to `{outputFolder}`
|
||||
|
||||
Deterministic creation:
|
||||
```bash
|
||||
agent_cmd="claude --dangerously-skip-permissions"
|
||||
if [ "$primary_agent" = "codex" ]; then agent_cmd="codex exec --full-auto"; fi
|
||||
|
||||
config_json=$(jq -n \
|
||||
--arg epic "$epic_id" \
|
||||
--arg epicName "$epic_name" \
|
||||
--argjson storyRange "$(echo "$range_json" | jq '.storyIds')" \
|
||||
--arg status "READY" \
|
||||
--arg currentStory "null" \
|
||||
--arg currentStep "preflight" \
|
||||
--arg aiCommand "$agent_cmd" \
|
||||
--arg customInstructions "$custom_instructions" \
|
||||
--argjson overrides "{\"skipAutomate\":$skip_automate,\"maxParallel\":$max_parallel}" \
|
||||
--argjson agentConfig "$agent_config_json" \
|
||||
'{epic:$epic,epicName:$epicName,storyRange:$storyRange,status:$status,currentStory:null,currentStep:$currentStep,aiCommand:$aiCommand,customInstructions:$customInstructions,overrides:$overrides,agentConfig:$agentConfig}'
|
||||
)
|
||||
|
||||
state_result=$("{buildStateDoc}" build-state-doc --template "{stateTemplate}" --output-folder "{outputFolder}" --config-json "$config_json")
|
||||
state_path=$(echo "$state_result" | jq -r '.path')
|
||||
```
|
||||
|
||||
Display: "**State document created.**"
|
||||
Record: `state_path` is the resolved `{outputFile}` for this run.
|
||||
|
||||
### 5. Auto-Proceed to Finalize
|
||||
|
||||
Persist any preflight notes to `{outputFile}`, update frontmatter (append `step-02-preflight` and `step-02a-preflight-config`, set `lastUpdated`).
|
||||
|
||||
---
|
||||
|
||||
## Then
|
||||
→ Load, read entire file, and execute `{nextStep}`
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
name: 'step-02b-preflight-finalize'
|
||||
description: 'Finalize preflight and start execution'
|
||||
nextStep: './step-03-execute.md'
|
||||
outputFolder: '{output_folder}/story-automator'
|
||||
outputFile: '{outputFolder}/orchestration-{epic_id}-{timestamp}.md'
|
||||
stateHelper: '../scripts/story-automator'
|
||||
ensureMarkerGitignore: '../scripts/story-automator'
|
||||
deriveProjectSlug: '../scripts/story-automator'
|
||||
markerFormat: '../data/marker-file-format.md'
|
||||
---
|
||||
|
||||
# Step 2b: Pre-flight Finalize
|
||||
|
||||
**Goal:** Finalize preflight artifacts, create marker, and start execution.
|
||||
**Interaction mode:** Deterministic auto-proceed.
|
||||
|
||||
---
|
||||
|
||||
## Do
|
||||
|
||||
### 1. Create Complexity + Agents Files
|
||||
|
||||
Derive deterministic filenames:
|
||||
```bash
|
||||
state_base=$(basename "{outputFile}" .md)
|
||||
complexity_path="{outputFolder}/complexity-${state_base}.json"
|
||||
agents_dir="{outputFolder}/agents"
|
||||
agents_path="$agents_dir/agents-${state_base}.md"
|
||||
```
|
||||
|
||||
Write complexity file:
|
||||
```bash
|
||||
mkdir -p "$(dirname "$complexity_path")"
|
||||
echo "$stories_json" | jq -c '{stories:.}' > "$complexity_path"
|
||||
```
|
||||
|
||||
Build deterministic agents file:
|
||||
```bash
|
||||
mkdir -p "$agents_dir"
|
||||
"{stateHelper}" orchestrator-helper agents-build \
|
||||
--state-file "{outputFile}" \
|
||||
--complexity-file "$complexity_path" \
|
||||
--output "$agents_path" \
|
||||
--config-json "$agent_config_json"
|
||||
```
|
||||
|
||||
Update state frontmatter with file paths:
|
||||
```bash
|
||||
agents_path_json=$(printf '%s' "$agents_path" | jq -R '.')
|
||||
complexity_path_json=$(printf '%s' "$complexity_path" | jq -R '.')
|
||||
"{stateHelper}" orchestrator-helper state-update "{outputFile}" \
|
||||
--set "agentsFile=$agents_path_json" \
|
||||
--set "complexityFile=$complexity_path_json"
|
||||
```
|
||||
|
||||
### 2. Create Marker and Begin Execution
|
||||
|
||||
**Create marker file** (see `{markerFormat}` for JSON structure):
|
||||
```bash
|
||||
# Resolve the active marker path for the selected runtime layout and gitignore it.
|
||||
marker_info=$("{stateHelper}" orchestrator-helper marker path)
|
||||
marker_entry=$(echo "$marker_info" | jq -r '.entry')
|
||||
"{ensureMarkerGitignore}" ensure-marker-gitignore --gitignore ".gitignore" --entry "$marker_entry"
|
||||
|
||||
# Create marker
|
||||
project_slug=$(echo "$("{deriveProjectSlug}" derive-project-slug --project-root "{project-root}")" | jq -r '.slug')
|
||||
"{stateHelper}" orchestrator-helper marker create --epic "$epic_id" --story "$first_story_id" \
|
||||
--remaining "$selected_count" --state-file "{outputFile}" \
|
||||
--project-slug "$project_slug" --pid "$$" --heartbeat "{timestamp}"
|
||||
```
|
||||
|
||||
Set status="IN_PROGRESS", log "Execution started".
|
||||
Update frontmatter (append `step-02b-preflight-finalize`, set `lastUpdated`).
|
||||
|
||||
---
|
||||
|
||||
## Then
|
||||
→ Load, read entire file, and execute `{nextStep}`
|
||||
199
.agents/skills/bmad-story-automator/steps-c/step-03-execute.md
Normal file
199
.agents/skills/bmad-story-automator/steps-c/step-03-execute.md
Normal file
@@ -0,0 +1,199 @@
|
||||
---
|
||||
name: 'step-03-execute'
|
||||
description: 'Autonomous execution loop - create and dev stories'
|
||||
nextStep: './step-03a-execute-review.md'
|
||||
dataFileIndex: '../data/data-file-index.md'
|
||||
scriptsDir: '../scripts/story-automator'
|
||||
outputFolder: '{output_folder}/story-automator'
|
||||
stateFilePattern: '{outputFolder}/orchestration-*.md'
|
||||
outputFile: '{outputFolder}/orchestration-{epic_id}-{timestamp}.md'
|
||||
retryStrategy: '../data/retry-fallback-strategy.md'
|
||||
executionPatterns: '../data/execution-patterns.md'
|
||||
subagentPrompts: '../data/subagent-prompts.md'
|
||||
---
|
||||
|
||||
## 🚨 CRITICAL: Load Data File Index FIRST
|
||||
|
||||
**BEFORE ANY EXECUTION**, load and read `{dataFileIndex}` completely.
|
||||
**DO NOT proceed until you have read the index and loaded the required files.**
|
||||
|
||||
---
|
||||
Set: `scripts="{scriptsDir}"`
|
||||
|
||||
## 🚨 CRITICAL: CLI Contract Check (Interface Drift Guard)
|
||||
|
||||
Before running any story loop logic, verify required helper commands/flags still exist.
|
||||
|
||||
```bash
|
||||
# Core command availability
|
||||
"$scripts" tmux-wrapper --help >/dev/null
|
||||
"$scripts" monitor-session --help >/dev/null
|
||||
"$scripts" orchestrator-helper --help >/dev/null
|
||||
|
||||
# Required spawn contract: --command must exist
|
||||
"$scripts" tmux-wrapper spawn --help | grep -q -- "--command"
|
||||
|
||||
# Build command contract must be available
|
||||
"$scripts" tmux-wrapper build-cmd --help >/dev/null
|
||||
```
|
||||
|
||||
If any check fails: **STOP and escalate immediately** with "helper CLI contract changed".
|
||||
|
||||
---
|
||||
|
||||
# Step 3: Execute Build Cycle
|
||||
|
||||
**Goal:** Autonomously execute all stories. Escalate only when decisions needed.
|
||||
**Interaction mode:** Deterministic autonomous execution.
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
Load from state document (located via `{stateFilePattern}`; output folder `{outputFolder}`; resolved path stored as `{outputFile}` for this run):
|
||||
- `storyRange`, `currentStory`, `currentStep`
|
||||
- `overrides` (skipAutomate, maxParallel)
|
||||
- `customInstructions`
|
||||
|
||||
Resolve agent configuration using deterministic agents file (see `{retryStrategy}` for full function):
|
||||
```bash
|
||||
state_file="{outputFile}"
|
||||
# resolve_agent_for_task "{task}" "$state_file" "{story_id}" -> sets primary_agent,fallback_agent
|
||||
```
|
||||
|
||||
**IF resuming** (currentStory set): Skip to that point in loop.
|
||||
**IF fresh**: Display "**Starting build cycle for {count} stories...**"
|
||||
|
||||
## 🚨 CRITICAL: Execution Patterns
|
||||
|
||||
**BEFORE executing any steps, read `{executionPatterns}` for:**
|
||||
- FORBIDDEN patterns (never chain multiple workflow steps)
|
||||
- REQUIRED patterns (verify state after each step)
|
||||
- Monitoring failure fallback sequence
|
||||
|
||||
**Key rule:** Each step (create/dev/auto/review) MUST be executed and monitored separately. NEVER chain steps in loops.
|
||||
|
||||
## Story Loop
|
||||
|
||||
> **⚠️ SPAWN PATTERN - READ THIS:**
|
||||
> Every `story-automator tmux-wrapper spawn` call **MUST** include `--command` with the built command:
|
||||
> ```bash
|
||||
> session=$("$scripts" tmux-wrapper spawn {step} {epic} {story_id} \
|
||||
> --agent "$agent" \
|
||||
> --command "$("$scripts" tmux-wrapper build-cmd {step} {story_id} --agent "$agent")")
|
||||
> ```
|
||||
> **Missing `--command` = session sits idle → `never_active` failure!**
|
||||
|
||||
**FOR EACH story in range:**
|
||||
|
||||
```bash
|
||||
"$scripts" orchestrator-helper state-update "$state_file" \
|
||||
--set currentStory={story_id} --set currentStep=step-03-execute \
|
||||
--set lastUpdated="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "- **[$(date -u +%Y-%m-%dT%H:%M:%SZ)]** Starting story {story_id}" >> "$state_file"
|
||||
|
||||
# Initialize Story Progress row
|
||||
tmp_state=$(mktemp)
|
||||
awk -v row="| {story_id} | - | - | - | - | - | in-progress |" '
|
||||
/^<!-- Progress rows -->$/ { print row }
|
||||
{ print }
|
||||
' "$state_file" > "$tmp_state" && mv "$tmp_state" "$state_file"
|
||||
```
|
||||
|
||||
Display: "**Story {N}/{total}: {title}**"
|
||||
Use compact operator output format for routine progress:
|
||||
```text
|
||||
[story {N}/{total}] {step} -> {state} (agent={agent}, retries={attempts})
|
||||
```
|
||||
After any session completes (create/dev/auto/review): `"$scripts" tmux-wrapper kill "$session"`
|
||||
|
||||
**MANDATORY log pre-filter (all sessions):** Before any deep parsing, pre-filter logs with a single grep/regex pass and pass only focused output forward.
|
||||
```bash
|
||||
log_file=$(echo "$result" | jq -r '.output_file')
|
||||
log_focus=$(grep -nE "SUCCESS|FAIL|ERROR|CRITICAL|WARN|RETRY|ESCALATE" "$log_file" | head -n 120)
|
||||
if [ -z "$log_focus" ]; then
|
||||
log_focus=$(tail -n 120 "$log_file")
|
||||
fi
|
||||
```
|
||||
If multiple logs exist, run one grep/regex pass across all log files and forward only matched lines + file names.
|
||||
|
||||
**Compact result contract (required):**
|
||||
- Return only: `next_action`, `confidence`, `error_class`, `retryable`, `reasons`, `session_id`
|
||||
- Do not pass full raw logs to parent flow unless escalation explicitly requires evidence payload
|
||||
|
||||
### A. Create Story
|
||||
*Skip if story file exists*
|
||||
|
||||
**Apply retry/fallback pattern from `{retryStrategy}`:** Up to 5 attempts, alternating agents, network-aware delays.
|
||||
|
||||
```bash
|
||||
# Retry loop: see {retryStrategy}
|
||||
session=$("$scripts" tmux-wrapper spawn create {epic} {story_id} \
|
||||
--agent "$current_agent" \
|
||||
--command "$("$scripts" tmux-wrapper build-cmd create {story_id} --agent "$current_agent" --state-file "$state_file")")
|
||||
result=$("$scripts" monitor-session "$session" --json --agent "$current_agent")
|
||||
"$scripts" tmux-wrapper kill "$session"
|
||||
validation=$("$scripts" orchestrator-helper verify-step create {story_id} --state-file "$state_file")
|
||||
```
|
||||
|
||||
- If `validation.verified == true`:
|
||||
```bash
|
||||
# Update Story Progress: mark create-story done
|
||||
tmp_state=$(mktemp)
|
||||
sed "s/^| ${story_id} |.*$/| ${story_id} | done | - | - | - | - | in-progress |/" "$state_file" > "$tmp_state" && mv "$tmp_state" "$state_file"
|
||||
```
|
||||
→ proceed to B
|
||||
- If `validation.verified == false` AND attempts < 5 → retry with next agent (see `{retryStrategy}`)
|
||||
- If `validation.verified == false` AND attempts == 5 → escalate (all retries exhausted)
|
||||
|
||||
### B. Dev Story
|
||||
|
||||
**Apply retry/fallback pattern from `{retryStrategy}`:** Up to 5 attempts, alternating agents.
|
||||
|
||||
```bash
|
||||
# Retry loop with agent alternation: see {retryStrategy}
|
||||
session=$("$scripts" tmux-wrapper spawn dev {epic} {story_id} \
|
||||
--agent "$current_agent" \
|
||||
--command "$("$scripts" tmux-wrapper build-cmd dev {story_id} --agent "$current_agent" --state-file "$state_file")")
|
||||
result=$("$scripts" monitor-session "$session" --json --agent "$current_agent")
|
||||
"$scripts" tmux-wrapper kill "$session"
|
||||
```
|
||||
|
||||
**Session Parsing Contract (required):**
|
||||
- Preferred: use Session Output Parser prompt from `{subagentPrompts}` on `result.output_file`
|
||||
- Fallback: use local parser below
|
||||
- Return normalized schema only: `next_action`, `confidence`, `error_class`, `reasons`
|
||||
|
||||
```bash
|
||||
parsed=$("$scripts" orchestrator-helper parse-output "$(printf '%s' "$result" | jq -r '.output_file')" dev)
|
||||
next_action=$(echo "$parsed" | jq -r '.next_action')
|
||||
confidence=$(echo "$parsed" | jq -r '.confidence // 0.0')
|
||||
error_class=$(echo "$parsed" | jq -r '.error_class // "none"')
|
||||
reasons=$(echo "$parsed" | jq -c '.reasons // []')
|
||||
```
|
||||
|
||||
- If `next_action == "proceed"`:
|
||||
```bash
|
||||
# Update Story Progress: mark dev-story done
|
||||
tmp_state=$(mktemp)
|
||||
sed "s/^| ${story_id} |.*$/| ${story_id} | done | done | - | - | - | in-progress |/" "$state_file" > "$tmp_state" && mv "$tmp_state" "$state_file"
|
||||
```
|
||||
→ proceed to C (next step)
|
||||
- If `next_action == "retry"` OR `result.final_state == "crashed"`:
|
||||
- Attempts < 5 → retry with next agent (see `{retryStrategy}`)
|
||||
- Plateau detected (same task 3x) → DEFER story, continue to next
|
||||
- Attempts == 5 → escalate (all retries exhausted)
|
||||
|
||||
## Auto-Proceed to Review Phase
|
||||
|
||||
Display: "**Dev story complete. Proceeding to automate and code review...**"
|
||||
|
||||
```bash
|
||||
"$scripts" orchestrator-helper state-update "$state_file" \
|
||||
--set currentStep=step-03a-execute-review \
|
||||
--set lastUpdated="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "- **[$(date -u +%Y-%m-%dT%H:%M:%SZ)]** Dev complete, proceeding to review phase" >> "$state_file"
|
||||
```
|
||||
|
||||
## Then
|
||||
→ Immediately load and execute `{nextStep}`
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
name: 'step-03a-execute-review'
|
||||
description: 'Autonomous execution loop - automate and code review'
|
||||
nextStep: './step-03b-execute-finish.md'
|
||||
scriptsDir: '../scripts/story-automator'
|
||||
outputFile: '{output_folder}/story-automator/orchestration-{epic_id}-{timestamp}.md'
|
||||
retryStrategy: '../data/retry-fallback-strategy.md'
|
||||
reviewLoop: '../data/code-review-loop.md'
|
||||
---
|
||||
|
||||
# Step 3a: Execute Review Phase
|
||||
|
||||
**Goal:** Run automate (guardrails) and code review loop for the current story.
|
||||
**Interaction mode:** Deterministic autonomous execution.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Step 3 completed (create-story and dev-story done)
|
||||
- State document updated with current story progress
|
||||
|
||||
Set: `scripts="{scriptsDir}"`
|
||||
|
||||
---
|
||||
|
||||
## Story Loop (Continue from Step 3)
|
||||
|
||||
### C. Automate (Guardrails)
|
||||
*Skip if `overrides.skipAutomate`*
|
||||
|
||||
**Apply retry/fallback pattern from `{retryStrategy}`:** Non-blocking, but still retry on failure.
|
||||
|
||||
```bash
|
||||
# --command required (see Spawn Pattern in step-03)
|
||||
session=$("$scripts" tmux-wrapper spawn auto {epic} {story_id} \
|
||||
--agent "$current_agent" \
|
||||
--command "$("$scripts" tmux-wrapper build-cmd auto {story_id} --agent "$current_agent" --state-file "$state_file")")
|
||||
result=$("$scripts" monitor-session "$session" --json --agent "$current_agent")
|
||||
"$scripts" tmux-wrapper kill "$session"
|
||||
```
|
||||
|
||||
- SUCCESS:
|
||||
```bash
|
||||
# Update Story Progress: mark automate done
|
||||
tmp_state=$(mktemp)
|
||||
sed "s/^| ${story_id} |.*$/| ${story_id} | done | done | done | - | - | in-progress |/" "{outputFile}" > "$tmp_state" && mv "$tmp_state" "{outputFile}"
|
||||
```
|
||||
Display: `[story {N}/{total}] automate -> done`
|
||||
→ proceed to D
|
||||
- FAILURE → retry up to 3 attempts (non-blocking, so fewer retries), then log warning:
|
||||
```bash
|
||||
# Update Story Progress: mark automate skipped
|
||||
tmp_state=$(mktemp)
|
||||
sed "s/^| ${story_id} |.*$/| ${story_id} | done | done | skip | - | - | in-progress |/" "{outputFile}" > "$tmp_state" && mv "$tmp_state" "{outputFile}"
|
||||
```
|
||||
Display: `[story {N}/{total}] automate -> skip (non-blocking)`
|
||||
→ proceed to D
|
||||
|
||||
### D. Code Review Loop
|
||||
|
||||
**See `{reviewLoop}` for complete script-based review cycle with v2.3 per-task agent configuration.**
|
||||
|
||||
**MANDATORY log-summary contract (every review cycle):**
|
||||
- Run a single grep/regex pass over review output first.
|
||||
- Return only compact fields to parent flow: `next_action`, `confidence`, `error_class`, `issues_count`, `top_issues`.
|
||||
- Do not carry full log payloads forward unless escalation requires raw evidence.
|
||||
|
||||
```bash
|
||||
review_log=$(echo "$result" | jq -r '.output_file')
|
||||
review_focus=$(grep -nE "SUCCESS|FAIL|ERROR|CRITICAL|WARN|RETRY|ESCALATE|ISSUE" "$review_log" | head -n 120)
|
||||
if [ -z "$review_focus" ]; then
|
||||
review_focus=$(tail -n 120 "$review_log")
|
||||
fi
|
||||
|
||||
# Compact subprocess-style summary contract for parent flow
|
||||
review_summary=$("$scripts" orchestrator-helper parse-output "$review_log" review --state-file "$state_file" | jq -c '
|
||||
{
|
||||
next_action: (.next_action // "retry"),
|
||||
confidence: (.confidence // 0),
|
||||
error_class: (.error_class // "unknown"),
|
||||
issues_count: ((.issues // []) | length),
|
||||
top_issues: ((.issues // [])[:3])
|
||||
}
|
||||
')
|
||||
```
|
||||
|
||||
Key points:
|
||||
- Up to 5 cycles using `story-automator tmux-wrapper spawn review` + `story-automator monitor-session`
|
||||
- **Agent:** Uses per-task config from state document (`resolve_agent_for_task "review"`)
|
||||
- **Verification:** Uses `--workflow review --story-key` for sprint-status verification
|
||||
- **States:** `completed` (verified):
|
||||
```bash
|
||||
# Update Story Progress: mark code-review done
|
||||
tmp_state=$(mktemp)
|
||||
sed "s/^| ${story_id} |.*$/| ${story_id} | done | done | done | done | - | in-progress |/" "{outputFile}" > "$tmp_state" && mv "$tmp_state" "{outputFile}"
|
||||
```
|
||||
Display: `[story {N}/{total}] review -> done`
|
||||
→ E | `incomplete` → count as failed attempt, retry until maxCycles, then CRITICAL escalate (Trigger #8)
|
||||
- Exit loop when sprint-status shows "done"
|
||||
- If `review_summary.next_action` is ambiguous, ask one clarifying question before escalating.
|
||||
|
||||
---
|
||||
|
||||
## Auto-Proceed to Finalization
|
||||
|
||||
Display: "**Code review complete. Proceeding to finalize commits and status checks...**"
|
||||
|
||||
```bash
|
||||
"$scripts" orchestrator-helper state-update "{outputFile}" \
|
||||
--set currentStep=step-03b-execute-finish \
|
||||
--set lastUpdated="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "- **[$(date -u +%Y-%m-%dT%H:%M:%SZ)]** Code review complete, proceeding to finalization" >> "{outputFile}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Then
|
||||
→ Immediately load and execute `{nextStep}`
|
||||
@@ -0,0 +1,173 @@
|
||||
---
|
||||
name: 'step-03b-execute-finish'
|
||||
description: 'Finalize each story (commit/status), trigger retrospective when epic complete, and finish execution loop'
|
||||
nextStep: './step-03c-execute-complete.md'
|
||||
scriptsDir: '../scripts/story-automator'
|
||||
outputFile: '{output_folder}/story-automator/orchestration-{epic_id}-{timestamp}.md'
|
||||
---
|
||||
|
||||
# Step 3b: Finalize Story + Wrap Execution
|
||||
|
||||
**Goal:** After code review completes for a story, commit changes, verify sprint status, update progress, and finish the loop.
|
||||
**Interaction mode:** Deterministic autonomous execution.
|
||||
|
||||
---
|
||||
|
||||
## Story Loop (Continue from Step 3)
|
||||
|
||||
### E. Git Commit
|
||||
|
||||
**Required:** Commit after every story (do not skip).
|
||||
|
||||
```bash
|
||||
commit=$("{scriptsDir}" commit-story --repo "{project-root}" --story {story_id} --title "{title}")
|
||||
ok=$(echo "$commit" | jq -r '.ok')
|
||||
```
|
||||
|
||||
- If `ok == true`:
|
||||
```bash
|
||||
# Update Story Progress: mark git-commit done
|
||||
tmp_state=$(mktemp)
|
||||
sed "s/^| ${story_id} |.*$/| ${story_id} | done | done | done | done | done | in-progress |/" "{outputFile}" > "$tmp_state" && mv "$tmp_state" "{outputFile}"
|
||||
```
|
||||
→ proceed to F
|
||||
- If `ok == false` → log warning and escalate
|
||||
|
||||
### F. Verify Sprint Status
|
||||
|
||||
```bash
|
||||
# Check sprint-status with story file fallback (v1.4.0)
|
||||
normalized=$("{scriptsDir}" orchestrator-helper normalize-key {story_id})
|
||||
story_key=$(echo "$normalized" | jq -r '.key')
|
||||
status=$("{scriptsDir}" orchestrator-helper sprint-status get "$story_key")
|
||||
is_done=$(echo "$status" | jq -r '.done')
|
||||
|
||||
# Fallback: trust story file if sprint-status disagrees
|
||||
if [ "$is_done" != "true" ]; then
|
||||
file_done=$("{scriptsDir}" orchestrator-helper story-file-status {story_id} | jq -r '.status')
|
||||
[ "$file_done" = "done" ] && is_done="true"
|
||||
fi
|
||||
```
|
||||
|
||||
- If `is_done == false` → return to Code Review Loop (Step 3, section D)
|
||||
- If `is_done == true` → proceed to G
|
||||
|
||||
### G. Story Complete
|
||||
Display: "**✅ Story {N} complete.**"
|
||||
```bash
|
||||
"{scriptsDir}" orchestrator-helper state-update "{outputFile}" \
|
||||
--set lastUpdated="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "- **[$(date -u +%Y-%m-%dT%H:%M:%SZ)]** Story {story_id}: ✅ complete (commit + sprint-status verified)" >> "{outputFile}"
|
||||
|
||||
# Update Story Progress: mark story done
|
||||
tmp_state=$(mktemp)
|
||||
sed "s/^| ${story_id} |.*$/| ${story_id} | done | done | done | done | done | done |/" "{outputFile}" > "$tmp_state" && mv "$tmp_state" "{outputFile}"
|
||||
```
|
||||
Display: `[story {N}/{total}] finalize -> done`
|
||||
|
||||
### H. Check Epic Completion & Trigger Retrospective (Multi-Epic Support)
|
||||
|
||||
After each story completes, check if ALL stories in this epic are now done. Retrospective only triggers when every story in the epic has passed code review and sprint status confirms all are "done".
|
||||
|
||||
#### H.1 Check All Stories Done
|
||||
|
||||
```bash
|
||||
# Run epic-level check in parallel with per-story checks
|
||||
tmp_epic_status=$(mktemp)
|
||||
("{scriptsDir}" orchestrator-helper sprint-status check-epic {epic_number} > "$tmp_epic_status") &
|
||||
epic_status_pid=$!
|
||||
|
||||
# Get all stories for this epic and verify each is done
|
||||
epic_stories=$("{scriptsDir}" orchestrator-helper get-epic-stories {epic_number} --state-file "{outputFile}")
|
||||
stories_ok=$(echo "$epic_stories" | jq -r '.ok')
|
||||
story_count=$(echo "$epic_stories" | jq -r '.count')
|
||||
all_done=true
|
||||
|
||||
if [ "$stories_ok" != "true" ] || [ "$story_count" -eq 0 ]; then
|
||||
all_done=false
|
||||
else
|
||||
tmp_story_checks=$(mktemp)
|
||||
echo "$epic_stories" | jq -r '.stories[]' \
|
||||
| xargs -I{} -P 4 sh -c '
|
||||
status=$("'"{scriptsDir}"'" orchestrator-helper sprint-status get "{}")
|
||||
done=$(echo "$status" | jq -r ".done")
|
||||
[ "$done" = "true" ] && echo "{}|done" || echo "{}|not_done"
|
||||
' > "$tmp_story_checks"
|
||||
|
||||
if rg -q '\|not_done$' "$tmp_story_checks"; then
|
||||
all_done=false
|
||||
fi
|
||||
rm -f "$tmp_story_checks"
|
||||
fi
|
||||
```
|
||||
|
||||
#### H.2 Secondary Verification via Sprint Status
|
||||
|
||||
```bash
|
||||
# Double-check: use result from parallel epic-level check
|
||||
wait "$epic_status_pid"
|
||||
epic_status=$(cat "$tmp_epic_status")
|
||||
rm -f "$tmp_epic_status"
|
||||
|
||||
epic_complete=$(echo "$epic_status" | jq -r '.allStoriesDone')
|
||||
epic_ok=$(echo "$epic_status" | jq -r '.ok')
|
||||
|
||||
# Both checks must pass
|
||||
if [ "$all_done" = "true" ] && [ "$epic_ok" = "true" ] && [ "$epic_complete" = "true" ]; then
|
||||
trigger_retro=true
|
||||
else
|
||||
trigger_retro=false
|
||||
fi
|
||||
```
|
||||
|
||||
#### H.3 Trigger Retrospective (Only When Epic Fully Complete)
|
||||
|
||||
**IF trigger_retro == true:**
|
||||
|
||||
1. Display: "**✅ Epic {epic_number} complete! All stories passed code review. Triggering retrospective (YOLO mode)...**"
|
||||
2. Log: `- **[{timestamp}]** Epic {epic_number}: ALL STORIES DONE - triggering retrospective`
|
||||
|
||||
```bash
|
||||
# CRITICAL: Use build-cmd to get full YOLO prompt with doc verification
|
||||
retro_agent=$("{scriptsDir}" orchestrator-helper retro-agent --state-file "{outputFile}" | jq -r '.primary')
|
||||
cmd=$("{scriptsDir}" tmux-wrapper build-cmd retro {epic_number} --agent "$retro_agent")
|
||||
session=$("{scriptsDir}" tmux-wrapper spawn retro "" {epic_number} --agent "$retro_agent" --command "$cmd")
|
||||
|
||||
# Monitor with safe failure (never escalate on retro failure)
|
||||
retro_timeout=60
|
||||
[ "$story_count" -gt 10 ] && retro_timeout=90
|
||||
result=$("{scriptsDir}" monitor-session "$session" --json --agent "$retro_agent" --timeout "$retro_timeout")
|
||||
"{scriptsDir}" tmux-wrapper kill "$session"
|
||||
|
||||
retro_status=$(echo "$result" | jq -r '.final_state')
|
||||
|
||||
if [ "$retro_status" = "completed" ] || [ "$retro_status" = "success" ]; then
|
||||
echo "- **[{timestamp}]** Epic {epic_number} retrospective: completed successfully" >> "{outputFile}"
|
||||
else
|
||||
echo "- **[{timestamp}]** Epic {epic_number} retrospective: skipped (reason: $retro_status)" >> "{outputFile}"
|
||||
fi
|
||||
```
|
||||
|
||||
3. Update state document with retrospective status:
|
||||
```yaml
|
||||
retrospectives:
|
||||
epic-{epic_number}:
|
||||
status: "completed" | "skipped"
|
||||
reason: "{reason_if_skipped}"
|
||||
timestamp: "{timestamp}"
|
||||
```
|
||||
|
||||
4. **Continue to next story regardless of retrospective result** (retrospectives never block)
|
||||
|
||||
**IF trigger_retro == false:**
|
||||
- Continue to next story (epic not yet complete)
|
||||
|
||||
**IMPORTANT RULES:**
|
||||
- **ALL stories must be done**: Retrospective only triggers when every story in the epic shows "done" in sprint status
|
||||
- **Use configured retro agent**: Resolve retrospective agent from `agentConfig` before spawn
|
||||
- **Never escalate; non-blocking**: If retrospective fails for any reason, log warning and continue
|
||||
|
||||
**END FOR EACH**
|
||||
|
||||
## Then
|
||||
→ After all stories complete, load and execute `{nextStep}`
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: 'step-03c-execute-complete'
|
||||
description: 'Post-loop completion summary, parallelism notes, and transition to wrapup'
|
||||
nextStep: './step-04-wrapup.md'
|
||||
scriptsDir: '../scripts/story-automator'
|
||||
outputFile: '{output_folder}/story-automator/orchestration-{epic_id}-{timestamp}.md'
|
||||
executionPatterns: '../data/execution-patterns.md'
|
||||
retryStrategy: '../data/retry-fallback-strategy.md'
|
||||
triggers: '../data/escalation-triggers.md'
|
||||
---
|
||||
|
||||
# Step 3c: Execution Complete
|
||||
|
||||
**Goal:** Summarize results after all stories finish, persist final status, and transition to wrapup.
|
||||
**Interaction mode:** Deterministic auto-proceed.
|
||||
|
||||
---
|
||||
|
||||
## All Complete
|
||||
|
||||
Display:
|
||||
```
|
||||
**All {count} stories completed!**
|
||||
|
||||
If `{count} <= 10`:
|
||||
| Story | Status |
|
||||
|-------|--------|
|
||||
{summary_table}
|
||||
|
||||
If `{count} > 10`:
|
||||
- Completed: {completed_count}
|
||||
- Warnings: {warning_count}
|
||||
- Escalations: {escalation_count}
|
||||
- See state log for full per-story table.
|
||||
|
||||
Proceeding to wrap-up...
|
||||
```
|
||||
|
||||
```bash
|
||||
"{scriptsDir}" orchestrator-helper state-update "{outputFile}" \
|
||||
--set status=EXECUTION_COMPLETE --set lastUpdated="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "- **[$(date -u +%Y-%m-%dT%H:%M:%SZ)]** All stories complete — execution finished" >> "{outputFile}"
|
||||
```
|
||||
|
||||
## Parallelism & Escalation
|
||||
|
||||
**Parallelism:** When `overrides.maxParallel > 1`, batch independent stories into concurrent groups:
|
||||
1. Check story dependency graph — only stories with no shared file dependencies can run in parallel
|
||||
2. Spawn up to `maxParallel` tmux sessions simultaneously (each runs steps A→F independently)
|
||||
3. Wait for all sessions in the batch to complete before starting the next batch
|
||||
4. Epic completion check (H) runs only after all batches finish
|
||||
|
||||
See `{executionPatterns}` for forbidden patterns and session isolation rules.
|
||||
|
||||
**Escalation:** See `{triggers}` for trigger definitions and `{retryStrategy}` for retry/fallback patterns. Escalation only after exhausting all retry attempts.
|
||||
|
||||
## Auto-Proceed to Wrap-up
|
||||
|
||||
Display: "**Execution loop complete. Proceeding to wrap-up...**"
|
||||
|
||||
```bash
|
||||
"{scriptsDir}" orchestrator-helper state-update "{outputFile}" \
|
||||
--set currentStep=step-04-wrapup \
|
||||
--set lastUpdated="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
```
|
||||
|
||||
## Then
|
||||
→ Immediately load and execute `{nextStep}`
|
||||
132
.agents/skills/bmad-story-automator/steps-c/step-04-wrapup.md
Normal file
132
.agents/skills/bmad-story-automator/steps-c/step-04-wrapup.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
name: 'step-04-wrapup'
|
||||
description: 'Finalize: summary, learnings, recommendations (terminal step)'
|
||||
learningsFile: '{output_folder}/story-automator/learnings.md'
|
||||
templates: '../data/wrapup-templates.md'
|
||||
stateFilePattern: '{output_folder}/story-automator/orchestration-*.md'
|
||||
outputFile: '{output_folder}/story-automator/orchestration-{epic_id}-{timestamp}.md'
|
||||
stateHelper: '../scripts/story-automator'
|
||||
stateMetrics: '../scripts/story-automator'
|
||||
reportRetentionPolicy: '../data/report-retention-policy.md'
|
||||
---
|
||||
|
||||
# Step 4: Wrap-up
|
||||
|
||||
**Goal:** Generate summary, capture learnings, finalize state.
|
||||
**Interaction mode:** Structured wrap-up with recommendation output.
|
||||
|
||||
---
|
||||
|
||||
## Do
|
||||
|
||||
### 1. Load Final State
|
||||
From state document (located via `{stateFilePattern}`; resolved path stored as `{outputFile}` for this run), extract:
|
||||
- Story progress table
|
||||
- Action log
|
||||
- Session references
|
||||
|
||||
Calculate:
|
||||
- Stories completed vs total
|
||||
- Code review cycles
|
||||
- Escalations encountered
|
||||
|
||||
Use the existing state document path from execution, and derive `story_range_csv` from frontmatter `storyRange`.
|
||||
|
||||
Deterministic metrics:
|
||||
```bash
|
||||
metrics=$("{stateMetrics}" state-metrics --state "{state_document_path}")
|
||||
```
|
||||
|
||||
Parallel optimization (metrics + retention policy extraction):
|
||||
```bash
|
||||
tmp_metrics=$(mktemp)
|
||||
tmp_retention=$(mktemp)
|
||||
|
||||
("{stateMetrics}" state-metrics --state "{state_document_path}" > "$tmp_metrics") &
|
||||
metrics_pid=$!
|
||||
|
||||
(awk '/^```bash/{flag=1;next}/^```/{flag=0}flag{print}' "{reportRetentionPolicy}" > "$tmp_retention") &
|
||||
retention_pid=$!
|
||||
|
||||
wait "$metrics_pid"
|
||||
wait "$retention_pid"
|
||||
|
||||
metrics=$(cat "$tmp_metrics")
|
||||
retention_cmds=$(cat "$tmp_retention")
|
||||
rm -f "$tmp_metrics" "$tmp_retention"
|
||||
```
|
||||
|
||||
**Optimization (data ops):** If action log exceeds 200 lines, use compact summary by default.
|
||||
```bash
|
||||
log_block=$(awk '/^## Action Log/{flag=1;next}/^## /{if(flag){exit}}flag{print}' "{state_document_path}")
|
||||
log_lines=$(printf "%s\n" "$log_block" | wc -l | tr -d ' ')
|
||||
if [ "$log_lines" -gt 200 ]; then
|
||||
log_focus=$(printf "%s\n" "$log_block" | tail -n 50)
|
||||
else
|
||||
log_focus="$log_block"
|
||||
fi
|
||||
```
|
||||
|
||||
### 2. Generate Summary
|
||||
From `{templates}`, use **Summary Report Template**.
|
||||
|
||||
Fill in all stats and display to user.
|
||||
|
||||
### 3. Capture Learnings
|
||||
Analyze run for patterns:
|
||||
- Common code review issues
|
||||
- Steps needing escalation
|
||||
- Timing patterns
|
||||
- What worked well
|
||||
|
||||
**IF `{learningsFile}` exists:** Load and merge
|
||||
**ELSE:** Create new
|
||||
|
||||
Append entry using **Learnings Entry Template** from `{templates}`.
|
||||
|
||||
### 4. Recommendations
|
||||
From `{templates}`, use **Recommendations Template**.
|
||||
|
||||
Present actionable suggestions based on patterns observed.
|
||||
|
||||
### 4b. Validation Report Housekeeping
|
||||
Load `{reportRetentionPolicy}` and apply its retention guidance when needed.
|
||||
|
||||
If validation report history is large, run the suggested maintenance command from that policy file.
|
||||
|
||||
### 5. Finalize State
|
||||
Update state document:
|
||||
- `status = 'COMPLETE'`
|
||||
- `completedAt = {timestamp}`
|
||||
- Append final summary to action log
|
||||
|
||||
Display: "**State document finalized.**"
|
||||
|
||||
### 6. Remove Marker File
|
||||
Remove the active runtime marker:
|
||||
```bash
|
||||
"{stateHelper}" orchestrator-helper marker remove
|
||||
```
|
||||
|
||||
This allows the Stop hook to stop normally after workflow completion by clearing the marker from the active runtime layout.
|
||||
|
||||
### 7. Workflow Complete
|
||||
|
||||
Display:
|
||||
```
|
||||
**🎉 Story Automator workflow complete!**
|
||||
|
||||
All stories have been processed through the build cycle.
|
||||
Retrospectives were triggered automatically when each epic completed (during execution loop).
|
||||
|
||||
State document: {outputFile}
|
||||
Learnings: {learningsFile}
|
||||
```
|
||||
|
||||
Persist final state to `{outputFile}`.
|
||||
|
||||
---
|
||||
|
||||
## End
|
||||
|
||||
**Workflow terminates here.** Retrospectives are now handled within the execution loop (step-03b) when each epic completes, not as a separate terminal step.
|
||||
Reference in New Issue
Block a user