diff --git a/README.md b/README.md index 783d1837..59feebb2 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,8 @@ Story Automator automates the BMAD implementation loop for one or more stories: 5. commit verified work 6. trigger retrospective when an epic is fully complete +Experimental quick-dev support is available as an alternate child step for environments that provide a `bmad-quick-dev` skill. In that mode the child workflow combines planning, coding, test generation, and review, while Automator still verifies story/sprint-status completion before commit. + The core runtime model is: - one orchestrator session diff --git a/docs/cli-reference.md b/docs/cli-reference.md index f361a0c2..f804819e 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -71,6 +71,8 @@ Critical rule: These commands are the orchestration control plane. +`tmux-wrapper build-cmd quick-dev ` is available as an experimental combined execution step when a `bmad-quick-dev` skill is installed. Monitor it with `monitor-session --workflow quick-dev --story-key ` so completion is verified against story/sprint-status artifacts. + ## Agent Config Commands - `agent-config list` diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index 73c80794..a4ea121f 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -116,6 +116,7 @@ make_required_skills() { printf '# checklist\n' >"$root/$skills_root/bmad-dev-story/checklist.md" make_skill "$root" bmad-retrospective "$skills_root" + make_skill "$root" bmad-quick-dev "$skills_root" } make_required_workflow_only_skills() { @@ -266,6 +267,9 @@ verify_common_install() { assert_contains 'build-cmd create {story_id} --agent "$current_agent" --model "$primary_model" --state-file "$state_file"' "$story_dir/steps-c/step-03-execute.md" assert_contains 'build-cmd dev {story_id} --agent "$current_agent" --state-file "$state_file"' "$story_dir/steps-c/step-03-execute.md" assert_contains 'build-cmd dev {story_id} --agent "$current_agent" --model "$primary_model" --state-file "$state_file"' "$story_dir/steps-c/step-03-execute.md" + assert_contains 'build-cmd quick-dev {story_id} --agent "$current_agent" --state-file "$state_file"' "$story_dir/steps-c/step-03-execute.md" + assert_contains 'build-cmd quick-dev {story_id} --agent "$current_agent" --model "$primary_model" --state-file "$state_file"' "$story_dir/steps-c/step-03-execute.md" + assert_contains 'workflow quick-dev --story-key {story_id} --state-file "$state_file"' "$story_dir/steps-c/step-03-execute.md" assert_contains 'build-cmd auto {story_id} --agent "$current_agent" --state-file "$state_file"' "$story_dir/steps-c/step-03a-execute-review.md" assert_contains 'build-cmd auto {story_id} --agent "$current_agent" --model "$primary_model" --state-file "$state_file"' "$story_dir/steps-c/step-03a-execute-review.md" assert_contains 'should_apply_primary_model' "$story_dir/data/retry-fallback-strategy.md" @@ -283,10 +287,11 @@ verify_qa_prompts() { local root="$1" local skills_root="${2:-.claude/skills}" local story_dir="$root/$skills_root/bmad-story-automator" - local auto_claude auto_codex review_claude retro_claude + local auto_claude auto_codex quick_dev_codex review_claude retro_claude auto_claude="$(cd "$root" && "$story_dir/scripts/story-automator" tmux-wrapper build-cmd auto 5.3 --agent claude)" auto_codex="$(cd "$root" && "$story_dir/scripts/story-automator" tmux-wrapper build-cmd auto 5.3 --agent codex)" + quick_dev_codex="$(cd "$root" && "$story_dir/scripts/story-automator" tmux-wrapper build-cmd quick-dev 5.3 --agent codex)" review_claude="$(cd "$root" && "$story_dir/scripts/story-automator" tmux-wrapper build-cmd review 5.3 --agent claude)" retro_claude="$(cd "$root" && "$story_dir/scripts/story-automator" tmux-wrapper build-cmd retro 5 --agent claude)" # Per-task model injection on both providers, including the bracketed `[1m]` @@ -303,6 +308,9 @@ verify_qa_prompts() { assert_string_contains "approval_policy=\"never\"" "$auto_codex" assert_string_contains "--disable plugins --disable sqlite --disable shell_snapshot" "$auto_codex" assert_string_contains "READ this skill first: $skills_root/bmad-qa-generate-e2e-tests/SKILL.md" "$auto_codex" + assert_string_contains "READ this skill first: $skills_root/bmad-quick-dev/SKILL.md" "$quick_dev_codex" + assert_string_contains "Quick Dev owns planning, coding, test generation, and review" "$quick_dev_codex" + assert_string_contains "use subagents for planning, implementation, test generation, and review where useful" "$quick_dev_codex" assert_string_contains "READ this skill first: $skills_root/bmad-story-automator-review/SKILL.md" "$review_claude" assert_string_contains "auto-fix all issues without prompting" "$review_claude" assert_string_contains "READ this skill first: $skills_root/bmad-retrospective/SKILL.md" "$retro_claude" diff --git a/skills/bmad-story-automator/data/orchestration-policy.json b/skills/bmad-story-automator/data/orchestration-policy.json index aa3cd607..7a9c8ebe 100644 --- a/skills/bmad-story-automator/data/orchestration-policy.json +++ b/skills/bmad-story-automator/data/orchestration-policy.json @@ -121,6 +121,29 @@ "contractFile": "/bmad-story-automator-review/contract.json" } }, + "quick-dev": { + "label": "quick-dev", + "assets": { + "skillName": "bmad-quick-dev", + "workflowCandidates": ["workflow.md", "workflow.yaml"], + "instructionsCandidates": [], + "checklistCandidates": ["checklist.md"], + "templateCandidates": [], + "required": [] + }, + "prompt": { + "templateFile": "data/prompts/quick-dev.md", + "interactionMode": "autonomous", + "defaultExtraInstruction": "use subagents for planning, implementation, test generation, and review where useful; complete the story without prompting" + }, + "parse": { + "schemaFile": "data/parse/quick-dev.json" + }, + "success": { + "verifier": "review_completion", + "contractFile": "/bmad-story-automator-review/contract.json" + } + }, "retro": { "label": "retrospective", "assets": { diff --git a/skills/bmad-story-automator/data/parse/quick-dev.json b/skills/bmad-story-automator/data/parse/quick-dev.json new file mode 100644 index 00000000..680666bd --- /dev/null +++ b/skills/bmad-story-automator/data/parse/quick-dev.json @@ -0,0 +1,11 @@ +{ + "requiredKeys": ["status", "story_done", "tests_passed", "critical_issues_remaining", "summary", "next_action"], + "schema": { + "status": "SUCCESS|FAILURE|AMBIGUOUS", + "story_done": "true|false", + "tests_passed": "true|false", + "critical_issues_remaining": "integer", + "summary": "brief description", + "next_action": "proceed|retry|escalate" + } +} diff --git a/skills/bmad-story-automator/data/prompts/quick-dev.md b/skills/bmad-story-automator/data/prompts/quick-dev.md new file mode 100644 index 00000000..8f43824e --- /dev/null +++ b/skills/bmad-story-automator/data/prompts/quick-dev.md @@ -0,0 +1,17 @@ +Execute the Quick Dev workflow for story {{story_id}}. + +{{skill_line}}{{workflow_line}}{{instructions_line}}{{checklist_line}}{{template_line}}Story file: _bmad-output/implementation-artifacts/{{story_prefix}}-*.md +Story ID: {{story_id}} + +Quick Dev owns planning, coding, test generation, and review for this story. + +Requirements: +- Use the existing BMAD story tracking artifacts: task checkboxes, Dev Agent Record, File List, story Status, and sprint-status.yaml. +- Use subagents for planning, implementation, test generation, and review when they materially improve throughput or quality. +- Keep subagent write scopes disjoint when running them in parallel. +- Run the relevant tests or verification commands. +- Update the story file and sprint-status.yaml through the normal story workflow contract. +- Mark the story done only when no critical issues remain. +- Do not wait for user input. + +{{extra_instruction}} diff --git a/skills/bmad-story-automator/data/scripts-reference.md b/skills/bmad-story-automator/data/scripts-reference.md index ec5436cd..4f4c3b48 100644 --- a/skills/bmad-story-automator/data/scripts-reference.md +++ b/skills/bmad-story-automator/data/scripts-reference.md @@ -72,6 +72,7 @@ model=$(echo "$selection" | jq -r '.model // ""') | `dev` | Implement story tasks | Claude, Codex | | `auto` | Test automation | Claude, Codex | | `review` | Code review with auto-fix | Claude, Codex | +| `quick-dev` | Combined planning, coding, tests, and review | Codex, Claude | | `retro` | Retrospective (YOLO mode) | Claude, Codex | ## Retrospective Commands (v1.5.0) diff --git a/skills/bmad-story-automator/data/workflow-commands.md b/skills/bmad-story-automator/data/workflow-commands.md index 654999de..332bf51f 100644 --- a/skills/bmad-story-automator/data/workflow-commands.md +++ b/skills/bmad-story-automator/data/workflow-commands.md @@ -41,6 +41,23 @@ Implement all tasks marked [ ]. Run tests. Update checkboxes."' Enter --- +## quick-dev + +```bash +tmux send-keys -t "SESSION" 'codex exec "Execute the Quick Dev workflow for story STORY_ID. + +READ this skill first: /bmad-quick-dev/SKILL.md +READ this workflow file next: /bmad-quick-dev/workflow.md +Story file: _bmad-output/implementation-artifacts/STORY_PREFIX-*.md +Use subagents for planning, implementation, test generation, and review where useful. +Complete the story without prompting. +Mark the story done only when no critical issues remain."' Enter +``` + +`quick-dev` is an experimental combined execution step. The monitor still verifies completion through the story/sprint-status done gate before Automator can commit. + +--- + ## code-review **MUST use the dedicated `bmad-story-automator-review` skill. Do NOT use a generic Task agent for reviews.** diff --git a/skills/bmad-story-automator/src/story_automator/commands/orchestrator.py b/skills/bmad-story-automator/src/story_automator/commands/orchestrator.py index 740335d7..cdf68352 100644 --- a/skills/bmad-story-automator/src/story_automator/commands/orchestrator.py +++ b/skills/bmad-story-automator/src/story_automator/commands/orchestrator.py @@ -110,7 +110,7 @@ def _usage(code: int) -> int: print(" get-epic-stories [--state-file path]", file=target) print(" check-blocking ", file=target) print(" agents-build --state-file path --complexity-file path --output path --config-json '{}'", file=target) - print(" agents-resolve (--state-file path | --agents-file path) --story ID --task create|dev|auto|review", file=target) + print(" agents-resolve (--state-file path | --agents-file path) --story ID --task create|dev|auto|review|quick-dev", file=target) print(" retro-agent --state-file path", file=target) return code diff --git a/skills/bmad-story-automator/src/story_automator/commands/orchestrator_epic_agents.py b/skills/bmad-story-automator/src/story_automator/commands/orchestrator_epic_agents.py index 88b5d5f3..687fec38 100644 --- a/skills/bmad-story-automator/src/story_automator/commands/orchestrator_epic_agents.py +++ b/skills/bmad-story-automator/src/story_automator/commands/orchestrator_epic_agents.py @@ -129,7 +129,7 @@ def agents_build_action(args: list[str]) -> int: for story in complexity.get("stories", []): level = str(story.get("complexity", {}).get("level", "medium")).lower() or "medium" tasks = {} - for task in ("create", "dev", "auto", "review"): + for task in ("create", "dev", "auto", "review", "quick-dev"): primary, fallback, model = resolve_agent(config, level, task) entry = { "primary": primary, diff --git a/skills/bmad-story-automator/src/story_automator/commands/state.py b/skills/bmad-story-automator/src/story_automator/commands/state.py index 38990141..63d4f61d 100644 --- a/skills/bmad-story-automator/src/story_automator/commands/state.py +++ b/skills/bmad-story-automator/src/story_automator/commands/state.py @@ -72,6 +72,7 @@ def cmd_build_state_doc(args: list[str]) -> int: text = re.sub( r"(?m)^overrides:\n(?:(?:\s{2}.*\n)*)", "overrides:\n" + f" executionMode: {json.dumps(str(overrides.get('executionMode') or 'split'))}\n" f" skipAutomate: {str(bool(overrides.get('skipAutomate', False))).lower()}\n" f" maxParallel: {int(overrides.get('maxParallel', 1) or 1)}\n", text, @@ -153,12 +154,13 @@ def cmd_build_state_doc(args: list[str]) -> int: for key, value in replacements.items(): text = re.sub(rf"(?m)^{re.escape(key)}:.*$", lambda m, k=key, v=value: f"{k}: {json.dumps(v)}", text) story_range = [item for item in config.get("storyRange", []) if isinstance(item, str)] - progress_rows = "\n".join(f"| {story_id} | ⏳ | ⏳ | ⏳ | ⏳ | ⏳ | pending |" for story_id in story_range) + progress_rows = "\n".join(f"| {story_id} | ⏳ | ⏳ | ⏳ | ⏳ | ⏳ | ⏳ | pending |" for story_id in story_range) body = { "{{epicName}}": str(config.get("epicName", "")), "{{epic}}": str(config.get("epic", "")), "{{storyRange}}": ", ".join(story_range), "{{createdAt}}": now, + "{{overrides.executionMode}}": str(overrides.get("executionMode") or "split"), "{{overrides.skipAutomate}}": str(bool(overrides.get("skipAutomate", False))).lower(), "{{overrides.maxParallel}}": str(int(overrides.get("maxParallel", 1) or 1)), "{{customInstructions}}": str(config.get("customInstructions", "")), diff --git a/skills/bmad-story-automator/src/story_automator/core/agent_config.py b/skills/bmad-story-automator/src/story_automator/core/agent_config.py index 19b67cd9..656da817 100644 --- a/skills/bmad-story-automator/src/story_automator/core/agent_config.py +++ b/skills/bmad-story-automator/src/story_automator/core/agent_config.py @@ -197,7 +197,7 @@ def build_agents_file(state_file: str | Path, complexity_file: str | Path, outpu for story in complexity_payload.get("stories", []): level = str(((story.get("complexity") or {}).get("level")) or "medium").strip().lower() or "medium" tasks = {} - for task in ("create", "dev", "auto", "review"): + for task in ("create", "dev", "auto", "review", "quick-dev"): primary, fallback, model = resolve_agent_for_task(config, level, task) entry: dict[str, Any] = { "primary": primary, diff --git a/skills/bmad-story-automator/src/story_automator/core/runtime_policy.py b/skills/bmad-story-automator/src/story_automator/core/runtime_policy.py index a0cd393e..992b12e9 100644 --- a/skills/bmad-story-automator/src/story_automator/core/runtime_policy.py +++ b/skills/bmad-story-automator/src/story_automator/core/runtime_policy.py @@ -10,7 +10,7 @@ from .utils import ensure_dir, get_project_root, iso_now, md5_hex8, read_text, write_atomic VALID_TOP_LEVEL_KEYS = {"version", "snapshot", "runtime", "workflow", "steps"} -VALID_STEP_NAMES = {"create", "dev", "auto", "review", "retro"} +VALID_STEP_NAMES = {"create", "dev", "auto", "review", "quick-dev", "retro"} VALID_VERIFIERS = {"create_story_artifact", "session_exit", "review_completion", "epic_complete"} VALID_ASSET_NAMES = {"skill", "workflow", "instructions", "checklist", "template"} VALID_PARSER_PROVIDERS = {"claude"} diff --git a/skills/bmad-story-automator/steps-c/step-02a-preflight-config.md b/skills/bmad-story-automator/steps-c/step-02a-preflight-config.md index 96d26595..8aff0bcb 100644 --- a/skills/bmad-story-automator/steps-c/step-02a-preflight-config.md +++ b/skills/bmad-story-automator/steps-c/step-02a-preflight-config.md @@ -140,7 +140,7 @@ config_json=$(jq -n \ --arg currentStep "preflight" \ --arg aiCommand "$agent_cmd" \ --arg customInstructions "$custom_instructions" \ - --argjson overrides "{\"skipAutomate\":$skip_automate,\"maxParallel\":$max_parallel}" \ + --argjson overrides "{\"executionMode\":\"split\",\"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}' ) diff --git a/skills/bmad-story-automator/steps-c/step-03-execute.md b/skills/bmad-story-automator/steps-c/step-03-execute.md index b7df134b..856210fb 100644 --- a/skills/bmad-story-automator/steps-c/step-03-execute.md +++ b/skills/bmad-story-automator/steps-c/step-03-execute.md @@ -71,7 +71,11 @@ state_file="{outputFile}" - 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. +**Key rule:** Each step in the selected execution mode MUST be executed and monitored separately. NEVER chain steps in loops. + +**Execution modes:** +- `split` (default): run create-story, dev-story, automate, and code-review as separate child sessions. +- `quick-dev` (experimental): run one Quick Dev child session per story, then verify the same story/sprint-status completion gate before finalization. ## Story Loop @@ -94,7 +98,7 @@ echo "- **[$(date -u +%Y-%m-%dT%H:%M:%SZ)]** Starting story {story_id}" >> "$sta # Initialize Story Progress row tmp_state=$(mktemp) -awk -v row="| {story_id} | - | - | - | - | - | in-progress |" ' +awk -v row="| {story_id} | - | - | - | - | - | - | in-progress |" ' /^$/ { print row } { print } ' "$state_file" > "$tmp_state" && mv "$tmp_state" "$state_file" @@ -121,6 +125,35 @@ If multiple logs exist, run one grep/regex pass across all log files and forward - 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 +### Quick Dev Mode + +If `overrides.executionMode == "quick-dev"`, skip sections A and B and run this section instead. + +```bash +resolve_agent_for_task "quick-dev" "$state_file" "{story_id}" +if should_apply_primary_model "$current_agent"; then + built_cmd=$("$scripts" tmux-wrapper build-cmd quick-dev {story_id} --agent "$current_agent" --model "$primary_model" --state-file "$state_file") +else + built_cmd=$("$scripts" tmux-wrapper build-cmd quick-dev {story_id} --agent "$current_agent" --state-file "$state_file") +fi +session=$("$scripts" tmux-wrapper spawn quick-dev {epic} {story_id} \ + --agent "$current_agent" \ + --command "$built_cmd") +result=$("$scripts" monitor-session "$session" --json --agent "$current_agent" --workflow quick-dev --story-key {story_id} --state-file "$state_file") +"$scripts" tmux-wrapper kill "$session" +validation=$("$scripts" orchestrator-helper verify-step quick-dev {story_id} --state-file "$state_file") +parsed=$("$scripts" orchestrator-helper parse-output "$(printf '%s' "$result" | jq -r '.output_file')" quick-dev --state-file "$state_file") +``` + +- If `validation.verified == true`: + ```bash + tmp_state=$(mktemp) + sed "s/^| ${story_id} |.*$/| ${story_id} | - | - | done | - | done | - | in-progress |/" "$state_file" > "$tmp_state" && mv "$tmp_state" "$state_file" + ``` + → skip `{nextStep}` and proceed directly to `step-03b-execute-finish.md` +- If `validation.verified == false` AND attempts < 5 → retry with next agent (see `{retryStrategy}`) +- If `validation.verified == false` AND attempts == 5 → escalate (all retries exhausted) + ### A. Create Story *Skip if story file exists* @@ -149,7 +182,7 @@ validation=$("$scripts" orchestrator-helper verify-step create {story_id} --stat ```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" + 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}`) @@ -191,7 +224,7 @@ reasons=$(echo "$parsed" | jq -c '.reasons // []') ```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" + 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"`: @@ -201,6 +234,8 @@ reasons=$(echo "$parsed" | jq -c '.reasons // []') ## Auto-Proceed to Review Phase +If `overrides.executionMode == "quick-dev"` and Quick Dev verification passed, skip this section and load `step-03b-execute-finish.md`. + Display: "**Dev story complete. Proceeding to automate and code review...**" ```bash diff --git a/skills/bmad-story-automator/steps-c/step-03a-execute-review.md b/skills/bmad-story-automator/steps-c/step-03a-execute-review.md index e38bb421..46f29aa3 100644 --- a/skills/bmad-story-automator/steps-c/step-03a-execute-review.md +++ b/skills/bmad-story-automator/steps-c/step-03a-execute-review.md @@ -50,7 +50,7 @@ result=$("$scripts" monitor-session "$session" --json --agent "$current_agent") ```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}" + 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 @@ -58,7 +58,7 @@ result=$("$scripts" monitor-session "$session" --json --agent "$current_agent") ```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}" + 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 @@ -99,7 +99,7 @@ Key points: ```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}" + 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) diff --git a/skills/bmad-story-automator/steps-c/step-03b-execute-finish.md b/skills/bmad-story-automator/steps-c/step-03b-execute-finish.md index 8da7759f..894fce5b 100644 --- a/skills/bmad-story-automator/steps-c/step-03b-execute-finish.md +++ b/skills/bmad-story-automator/steps-c/step-03b-execute-finish.md @@ -28,7 +28,12 @@ ok=$(echo "$commit" | jq -r '.ok') ```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}" + execution_mode=$(awk '/^overrides:/ {in_overrides=1; next} in_overrides && /^[^ ]/ {in_overrides=0} in_overrides && /executionMode:/ {gsub(/["'\'']/, "", $2); print $2; exit}' "{outputFile}") + if [ "${execution_mode:-split}" = "quick-dev" ]; then + sed "s/^| ${story_id} |.*$/| ${story_id} | - | - | done | - | done | done | in-progress |/" "{outputFile}" > "$tmp_state" && mv "$tmp_state" "{outputFile}" + else + sed "s/^| ${story_id} |.*$/| ${story_id} | done | done | - | done | done | done | in-progress |/" "{outputFile}" > "$tmp_state" && mv "$tmp_state" "{outputFile}" + fi ``` → proceed to F - If `ok == false` → log warning and escalate @@ -61,7 +66,12 @@ echo "- **[$(date -u +%Y-%m-%dT%H:%M:%SZ)]** Story {story_id}: ✅ complete (com # 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}" +execution_mode=$(awk '/^overrides:/ {in_overrides=1; next} in_overrides && /^[^ ]/ {in_overrides=0} in_overrides && /executionMode:/ {gsub(/["'\'']/, "", $2); print $2; exit}' "{outputFile}") +if [ "${execution_mode:-split}" = "quick-dev" ]; then + sed "s/^| ${story_id} |.*$/| ${story_id} | - | - | done | - | done | done | done |/" "{outputFile}" > "$tmp_state" && mv "$tmp_state" "{outputFile}" +else + sed "s/^| ${story_id} |.*$/| ${story_id} | done | done | - | done | done | done | done |/" "{outputFile}" > "$tmp_state" && mv "$tmp_state" "{outputFile}" +fi ``` Display: `[story {N}/{total}] finalize -> done` diff --git a/skills/bmad-story-automator/templates/state-document.md b/skills/bmad-story-automator/templates/state-document.md index de50b019..52d23e03 100644 --- a/skills/bmad-story-automator/templates/state-document.md +++ b/skills/bmad-story-automator/templates/state-document.md @@ -13,6 +13,7 @@ createdAt: "" # Configuration aiCommand: "" # Deprecated: use agentConfig overrides: + executionMode: "split" # split | quick-dev skipAutomate: false maxParallel: 1 customInstructions: "" # User-provided instructions for orchestration @@ -74,6 +75,7 @@ completedSessions: [] **Created:** {{createdAt}} **Overrides:** +- Execution Mode: {{overrides.executionMode}} - Skip Automate: {{overrides.skipAutomate}} - Max Parallel: {{overrides.maxParallel}} @@ -84,8 +86,8 @@ completedSessions: [] ## Story Progress -| Story | create-story | dev-story | automate | code-review | git-commit | Status | -|-------|--------------|-----------|----------|-------------|------------|--------| +| Story | create-story | dev-story | quick-dev | automate | code-review | git-commit | Status | +|-------|--------------|-----------|-----------|----------|-------------|------------|--------| --- diff --git a/tests/test_agent_config_model.py b/tests/test_agent_config_model.py index 32f27469..bacc0bae 100644 --- a/tests/test_agent_config_model.py +++ b/tests/test_agent_config_model.py @@ -216,6 +216,8 @@ def test_build_agents_file_includes_model(self) -> None: self.assertEqual(tasks["dev"]["model"], "claude-opus-4-7[1m]") self.assertNotIn("model", tasks["create"]) self.assertNotIn("model", tasks["auto"]) + self.assertIn("quick-dev", tasks) + self.assertNotIn("model", tasks["quick-dev"]) def test_resolve_agents_returns_model(self) -> None: with tempfile.TemporaryDirectory() as tmp: @@ -588,6 +590,16 @@ def test_build_cmd_without_model_unchanged(self) -> None: rendered = stdout.getvalue() self.assertNotIn("--model", rendered) + def test_build_cmd_renders_quick_dev_prompt(self) -> None: + stdout = io.StringIO() + with patch.dict(os.environ, {"PROJECT_ROOT": str(self.project_root), "AI_AGENT": "codex"}, clear=False), redirect_stdout(stdout): + code = _build_cmd(["quick-dev", "9.1", "--agent", "codex"]) + self.assertEqual(code, 0) + rendered = stdout.getvalue() + self.assertIn("Execute the Quick Dev workflow for story 9.1.", rendered) + self.assertIn("READ this skill first: .claude/skills/bmad-quick-dev/SKILL.md", rendered) + self.assertIn("use subagents for planning, implementation, test generation, and review where useful", rendered) + def _install_bundle(self) -> None: source_skill = REPO_ROOT / "skills" / "bmad-story-automator" source_review = REPO_ROOT / "skills" / "bmad-story-automator-review" @@ -597,7 +609,7 @@ def _install_bundle(self) -> None: shutil.copytree(source_review, target_root / "bmad-story-automator-review") def _install_required_skills(self) -> None: - for name in ("bmad-create-story", "bmad-dev-story", "bmad-retrospective", "bmad-qa-generate-e2e-tests"): + for name in ("bmad-create-story", "bmad-dev-story", "bmad-retrospective", "bmad-qa-generate-e2e-tests", "bmad-quick-dev"): skill_dir = self.project_root / ".claude" / "skills" / name skill_dir.mkdir(parents=True, exist_ok=True) (skill_dir / "SKILL.md").write_text(f"# {name}\n", encoding="utf-8") diff --git a/tests/test_runtime_policy.py b/tests/test_runtime_policy.py index b3d9b475..e9911fe5 100644 --- a/tests/test_runtime_policy.py +++ b/tests/test_runtime_policy.py @@ -33,6 +33,8 @@ def test_bundled_default_loads(self) -> None: policy = load_effective_policy(str(self.project_root)) self.assertEqual(policy["version"], 1) self.assertEqual(policy["steps"]["review"]["success"]["verifier"], "review_completion") + self.assertEqual(policy["steps"]["quick-dev"]["success"]["verifier"], "review_completion") + self.assertEqual(policy["steps"]["quick-dev"]["assets"]["files"]["skill"], "") def test_project_override_deep_merges_and_arrays_replace(self) -> None: self._write_override( diff --git a/tests/test_success_verifiers.py b/tests/test_success_verifiers.py index ea2d1d28..d44e72a5 100644 --- a/tests/test_success_verifiers.py +++ b/tests/test_success_verifiers.py @@ -464,6 +464,19 @@ def test_monitor_dispatch_uses_review_verifier_from_contract(self) -> None: self.assertEqual(verifier, "review_completion") self.assertTrue(payload["verified"]) + def test_monitor_dispatch_uses_quick_dev_verifier_from_contract(self) -> None: + self._write_story("1-2-example", status="done") + result = _verify_monitor_completion( + "quick-dev", + project_root=str(self.project_root), + story_key="1.2", + output_file="/tmp/session.txt", + ) + self.assertIsNotNone(result) + payload, verifier = result or ({}, "") + self.assertEqual(verifier, "review_completion") + self.assertTrue(payload["verified"]) + def test_monitor_dispatch_skips_story_keyed_verifier_without_story_key(self) -> None: result = _verify_monitor_completion( "review",