feat: implement workflow yaml validation and schema
This commit is contained in:
parent
9d510fc075
commit
229fb8240b
|
|
@ -43,9 +43,9 @@
|
|||
"test": "npm run test:schemas && npm run test:install && npm run validate:bundles && npm run validate:schemas && npm run lint && npm run format:check",
|
||||
"test:coverage": "c8 --reporter=text --reporter=html npm run test:schemas",
|
||||
"test:install": "node test/test-installation-components.js",
|
||||
"test:schemas": "node test/test-agent-schema.js",
|
||||
"test:schemas": "node test/test-agent-schema.js && node test/test-workflow-schema.js",
|
||||
"validate:bundles": "node tools/validate-bundles.js",
|
||||
"validate:schemas": "node tools/validate-agent-schema.js"
|
||||
"validate:schemas": "node tools/validate-agent-schema.js && node tools/validate-workflow-schema.js"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,cjs,mjs}": [
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Brainstorming Session Workflow Configuration
|
||||
name: "brainstorming"
|
||||
description: "Facilitate interactive brainstorming sessions using diverse creative techniques. This workflow facilitates interactive brainstorming sessions using diverse creative techniques. The session is highly interactive, with the AI acting as a facilitator to guide the user through various ideation methods to generate and refine creative solutions."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/cis/config.yaml"
|
||||
|
|
@ -27,7 +26,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "brainstorming"
|
||||
description: "Facilitate interactive brainstorming sessions using diverse creative techniques. This workflow facilitates interactive brainstorming sessions using diverse creative techniques. The session is highly interactive, with the AI acting as a facilitator to guide the user through various ideation methods to generate and refine creative solutions."
|
||||
author: "BMad"
|
||||
template: "{bmad_folder}/core/workflows/brainstorming/template.md"
|
||||
instructions: "{bmad_folder}/core/workflows/brainstorming/instructions.md"
|
||||
brain_techniques: "{bmad_folder}/core/workflows/brainstorming/brain-methods.csv"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Party Mode - Multi-Agent Group Discussion Workflow
|
||||
name: "party-mode"
|
||||
description: "Orchestrates group discussions between all installed BMAD agents, enabling natural multi-agent conversations"
|
||||
author: "BMad"
|
||||
|
||||
# Critical data sources - manifest and config overrides
|
||||
agent_manifest: "{project-root}/{bmad_folder}/_cfg/agent-manifest.csv"
|
||||
|
|
@ -20,7 +19,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "party-mode"
|
||||
description: "Orchestrates group discussions between all installed BMAD agents, enabling natural multi-agent conversations"
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/core/workflows/party-mode/instructions.md"
|
||||
agent_manifest: "{bmad_folder}/_cfg/agent-manifest.csv"
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Audit Workflow Configuration
|
||||
name: "audit-workflow"
|
||||
description: "Comprehensive workflow quality audit - validates structure, config standards, variable usage, bloat detection, and web_bundle completeness. Performs deep analysis of workflow.yaml, instructions.md, template.md, and web_bundle configuration against BMAD v6 standards."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmb/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Convert Legacy - BMAD v4 to v6 Converter Configuration
|
||||
name: "convert-legacy"
|
||||
description: "Converts legacy BMAD v4 or similar items (agents, workflows, modules) to BMad Core compliant format with proper structure and conventions"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/bmb/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Build Agent Workflow Configuration
|
||||
name: create-agent
|
||||
description: "Interactive workflow to build BMAD Core compliant agents (YAML source compiled to .md during install) with optional brainstorming, persona development, and command structure"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/bmb/config.yaml"
|
||||
|
|
@ -42,7 +41,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "create-agent"
|
||||
description: "Interactive workflow to build BMAD Core compliant agents (simple, expert, or module types) with optional brainstorming for agent ideas, proper persona development, activation rules, and command structure"
|
||||
author: "BMad"
|
||||
web_bundle_files:
|
||||
- "{bmad_folder}/bmb/workflows/create-agent/instructions.md"
|
||||
- "{bmad_folder}/bmb/workflows/create-agent/checklist.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Build Module Workflow Configuration
|
||||
name: create-module
|
||||
description: "Interactive workflow to build complete BMAD modules with agents, workflows, tasks, and installation infrastructure"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/bmb/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# {TITLE} Workflow Template Configuration
|
||||
name: "{WORKFLOW_CODE}"
|
||||
description: "{WORKFLOW_DESCRIPTION}"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
# Add Additional Config Pulled Variables Here
|
||||
|
|
@ -37,7 +36,6 @@ required_tools: #optional, can be omitted
|
|||
web_bundle: #optional, can be omitted
|
||||
name: "{WORKFLOW_CODE}"
|
||||
description: "{WORKFLOW_DESCRIPTION}"
|
||||
author: "BMad"
|
||||
|
||||
# Core workflow files (paths relative to {bmad_folder}/ root)
|
||||
instructions: "{bmad_folder}/{module-code}/workflows/{workflow-code}/instructions.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Build Workflow - Workflow Builder Configuration
|
||||
name: create-workflow
|
||||
description: "Interactive workflow builder that guides creation of new BMAD workflows with proper structure and validation for optimal human-AI collaboration. Includes optional brainstorming phase for workflow ideas and design."
|
||||
author: "BMad Builder"
|
||||
|
||||
# Critical variables
|
||||
config_source: "{project-root}/{bmad_folder}/bmb/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Edit Agent - Agent Editor Configuration
|
||||
name: "edit-agent"
|
||||
description: "Edit existing BMAD agents while following all best practices and conventions"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/bmb/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Edit Module - Module Editor Configuration
|
||||
name: "edit-module"
|
||||
description: "Edit existing BMAD modules (structure, agents, workflows, documentation) while following all best practices"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/bmb/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Edit Workflow - Workflow Editor Configuration
|
||||
name: "edit-workflow"
|
||||
description: "Edit existing BMAD workflows while following all best practices and conventions"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/bmb/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Module Brief Workflow Configuration
|
||||
name: module-brief
|
||||
description: "Create a comprehensive Module Brief that serves as the blueprint for building new BMAD modules using strategic analysis and creative vision"
|
||||
author: "BMad Builder"
|
||||
|
||||
# Critical variables
|
||||
config_source: "{project-root}/{bmad_folder}/bmb/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Brainstorm Game Workflow Configuration
|
||||
name: "brainstorm-game"
|
||||
description: "Facilitate game brainstorming sessions by orchestrating the CIS brainstorming workflow with game-specific context, guidance, and additional game design techniques."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
@ -29,7 +28,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "brainstorm-game"
|
||||
description: "Facilitate game brainstorming sessions by orchestrating the CIS brainstorming workflow with game-specific context, guidance, and additional game design techniques."
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/bmgd/workflows/1-preproduction/brainstorm-game/instructions.md"
|
||||
template: false
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Game Brief - Interactive Workflow Configuration
|
||||
name: game-brief
|
||||
description: "Interactive game brief creation workflow that guides users through defining their game vision with multiple input sources and conversational collaboration"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
@ -26,7 +25,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "game-brief"
|
||||
description: "Interactive game brief creation workflow that guides users through defining their game vision with multiple input sources and conversational collaboration"
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/bmgd/workflows/1-preproduction/game-brief/instructions.md"
|
||||
validation: "{bmad_folder}/bmgd/workflows/1-preproduction/game-brief/checklist.md"
|
||||
template: "{bmad_folder}/bmgd/workflows/1-preproduction/game-brief/template.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Game Design Document (GDD) Workflow
|
||||
name: gdd
|
||||
description: "Game Design Document workflow for all game project levels - from small prototypes to full AAA games. Generates comprehensive GDD with game mechanics, systems, progression, and implementation guidance."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
@ -49,7 +48,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "gdd"
|
||||
description: "Game Design Document workflow for all game project levels - from small prototypes to full AAA games. Generates comprehensive GDD with game mechanics, systems, progression, and implementation guidance."
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/bmgd/workflows/2-design/gdd/instructions-gdd.md"
|
||||
web_bundle_files:
|
||||
- "{bmad_folder}/bmgd/workflows/2-design/gdd/instructions-gdd.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Narrative Design Workflow
|
||||
name: narrative
|
||||
description: "Narrative design workflow for story-driven games and applications. Creates comprehensive narrative documentation including story structure, character arcs, dialogue systems, and narrative implementation guidance."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
@ -25,7 +24,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "narrative"
|
||||
description: "Narrative design workflow for story-driven games and applications. Creates comprehensive narrative documentation including story structure, character arcs, dialogue systems, and narrative implementation guidance."
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/bmgd/workflows/2-design/narrative/instructions-narrative.md"
|
||||
web_bundle_files:
|
||||
- "{bmad_folder}/bmgd/workflows/2-design/narrative/instructions-narrative.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Game Architecture Workflow Configuration
|
||||
name: game-architecture
|
||||
description: "Collaborative game architecture workflow for AI-agent consistency. Intelligent, adaptive conversation that produces a decision-focused game architecture document covering engine, systems, networking, and technical design optimized for game development."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
@ -53,7 +52,6 @@ pattern_categories: "{installed_path}/pattern-categories.csv"
|
|||
default_output_file: "{output_folder}/game-architecture.md"
|
||||
|
||||
# Workflow metadata
|
||||
version: "1.3.2"
|
||||
replaces: "architecture"
|
||||
paradigm: "facilitation-driven"
|
||||
execution_time: "30-90 minutes depending on user skill level"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Review Story Workflow
|
||||
name: code-review
|
||||
description: "Perform a Senior Developer code review on a completed story flagged Ready for Review, leveraging story-context, epic tech-spec, repo docs, MCP servers for latest best-practices, and web search as fallback. Appends structured review notes to the story."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Correct Course - Sprint Change Management Workflow
|
||||
name: "correct-course"
|
||||
description: "Navigate significant changes during sprint execution by analyzing impact, proposing solutions, and routing for implementation"
|
||||
author: "BMad Method"
|
||||
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
output_folder: "{config_source}:output_folder"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: create-story
|
||||
description: "Create the next user story markdown from epics/PRD and architecture, using a standard template and saving to the stories folder"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: dev-story
|
||||
description: "Execute a story by implementing tasks/subtasks, writing tests, validating, and updating the story file per acceptance criteria"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: epic-tech-context
|
||||
description: "Generate a comprehensive Technical Specification from PRD and Architecture with acceptance criteria and traceability mapping"
|
||||
author: "BMAD BMM"
|
||||
|
||||
# Critical variables
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Retrospective - Epic Completion Review Workflow
|
||||
name: "retrospective"
|
||||
description: "Run after epic completion to review overall success, extract lessons learned, and explore if new information emerged that might impact the next epic"
|
||||
author: "BMad"
|
||||
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
output_folder: "{config_source}:output_folder"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: sprint-planning
|
||||
description: "Generate and manage the sprint status tracking file for Phase 4 implementation, extracting all epics and stories from epic files and tracking their status through the development lifecycle"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Story Context Creation Workflow
|
||||
name: story-context
|
||||
description: "Assemble a dynamic Story Context XML by pulling latest documentation and existing code/library artifacts relevant to a drafted story"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Story Done Workflow (DEV Agent)
|
||||
name: story-done
|
||||
description: "Marks a story as done (DoD complete) and moves it from its current status → DONE in the status file. Advances the story queue. Simple status-update workflow with no searching required."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Story Ready Workflow (SM Agent)
|
||||
name: story-ready
|
||||
description: "Marks a drafted story as ready for development and moves it from TODO → IN PROGRESS in the status file. Simple status-update workflow with no searching required."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmgd/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Brainstorm Project Workflow Configuration
|
||||
name: "brainstorm-project"
|
||||
description: "Facilitate project brainstorming sessions by orchestrating the CIS brainstorming workflow with project-specific context and guidance."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
@ -28,7 +27,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "brainstorm-project"
|
||||
description: "Facilitate project brainstorming sessions by orchestrating the CIS brainstorming workflow with project-specific context and guidance."
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/bmm/workflows/1-analysis/brainstorm-project/instructions.md"
|
||||
template: false
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Domain Research Workflow Configuration
|
||||
name: domain-research
|
||||
description: "Collaborative exploration of domain-specific requirements, regulations, and patterns for complex projects"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
@ -29,7 +28,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "domain-research"
|
||||
description: "Collaborative exploration of domain-specific requirements, regulations, and patterns for complex projects"
|
||||
author: "BMad"
|
||||
|
||||
# Core workflow files ({bmad_folder}/-relative paths)
|
||||
instructions: "{bmad_folder}/bmm/workflows/1-analysis/domain-research/instructions.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Product Brief - Interactive Workflow Configuration
|
||||
name: product-brief
|
||||
description: "Interactive product brief creation workflow that guides users through defining their product vision with multiple input sources and conversational collaboration"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
@ -47,7 +46,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "product-brief"
|
||||
description: "Interactive product brief creation workflow that guides users through defining their product vision with multiple input sources and conversational collaboration"
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/bmm/workflows/1-analysis/product-brief/instructions.md"
|
||||
validation: "{bmad_folder}/bmm/workflows/1-analysis/product-brief/checklist.md"
|
||||
template: "{bmad_folder}/bmm/workflows/1-analysis/product-brief/template.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Research Workflow - Multi-Type Research System
|
||||
name: research
|
||||
description: "Adaptive research workflow supporting multiple research types: market research, deep research prompt generation, technical/architecture evaluation, competitive intelligence, user research, and domain analysis"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
@ -46,7 +45,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "research"
|
||||
description: "Adaptive research workflow supporting multiple research types: market research, deep research prompt generation, technical/architecture evaluation, competitive intelligence, user research, and domain analysis"
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/bmm/workflows/1-analysis/research/instructions-router.md" # Router loads specific instruction sets
|
||||
validation: "{bmad_folder}/bmm/workflows/1-analysis/research/checklist.md"
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Create UX Design Workflow Configuration
|
||||
name: create-ux-design
|
||||
description: "Collaborative UX design facilitation workflow that creates exceptional user experiences through visual exploration and informed decision-making. Unlike template-driven approaches, this workflow facilitates discovery, generates visual options, and collaboratively designs the UX with the user at every step."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
@ -62,7 +61,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "create-ux-design"
|
||||
description: "Collaborative UX design facilitation workflow that creates exceptional user experiences through visual exploration and informed decision-making. Unlike template-driven approaches, this workflow facilitates discovery, generates visual options, and collaboratively designs the UX with the user at every step."
|
||||
author: "BMad"
|
||||
|
||||
# Core workflow files ({bmad_folder}/-relative paths)
|
||||
instructions: "{bmad_folder}/bmm/workflows/2-plan-workflows/create-ux-design/instructions.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Product Requirements Document (PRD) Workflow
|
||||
name: prd
|
||||
description: "Unified PRD workflow for BMad Method and Enterprise Method tracks. Produces strategic PRD and tactical epic breakdown. Hands off to architecture workflow for technical design. Note: Quick Flow track uses tech-spec workflow."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
@ -54,7 +53,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "prd"
|
||||
description: "Unified PRD workflow for BMad Method and Enterprise Method tracks. Produces strategic PRD and tactical epic breakdown. Hands off to architecture workflow for technical design. Note: Quick Flow track uses tech-spec workflow."
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/bmm/workflows/2-plan-workflows/prd/instructions.md"
|
||||
validation: "{bmad_folder}/bmm/workflows/2-plan-workflows/prd/checklist.md"
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Technical Specification
|
||||
name: tech-spec
|
||||
description: "Technical specification workflow for quick-flow projects. Creates focused tech spec and generates epic + stories (1 story for simple changes, 2-5 stories for features). Tech-spec only - no PRD needed."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Architecture Workflow Configuration
|
||||
name: architecture
|
||||
description: "Collaborative architectural decision facilitation for AI-agent consistency. Replaces template-driven architecture with intelligent, adaptive conversation that produces a decision-focused architecture document optimized for preventing agent conflicts."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
@ -56,7 +55,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "architecture"
|
||||
description: "Collaborative architectural decision facilitation for AI-agent consistency. Replaces template-driven architecture with intelligent, adaptive conversation that produces a decision-focused architecture document optimized for preventing agent conflicts."
|
||||
author: "BMad"
|
||||
|
||||
# Core workflow files ({bmad_folder}/-relative paths)
|
||||
instructions: "{bmad_folder}/bmm/workflows/3-solutioning/architecture/instructions.md"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Epic and Story Decomposition Workflow
|
||||
name: create-epics-and-stories
|
||||
description: "Transform PRD requirements into bite-sized stories organized into deliverable functional epics. This workflow takes a Product Requirements Document (PRD) and breaks it down into epics and user stories that can be easily assigned to development teams. It ensures that all functional requirements are captured in a structured format, making it easier for teams to understand and implement the necessary features."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
@ -55,7 +54,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "create-epics-and-stories"
|
||||
description: "Transform PRD requirements into bite-sized stories organized in epics for 200k context dev agents"
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/bmm/workflows/3-solutioning/create-epics-and-stories/instructions.md"
|
||||
template: "{bmad_folder}/bmm/workflows/3-solutioning/create-epics-and-stories/epics-template.md"
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Implementation Readiness - Workflow Configuration
|
||||
name: implementation-readiness
|
||||
description: "Validate that PRD, UX Design, Architecture, Epics and Stories are complete and aligned before Phase 4 implementation. Ensures all artifacts cover the MVP requirements with no gaps or contradictions."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Review Story Workflow
|
||||
name: code-review
|
||||
description: "Perform a Senior Developer code review on a completed story flagged Ready for Review, leveraging story-context, epic tech-spec, repo docs, MCP servers for latest best-practices, and web search as fallback. Appends structured review notes to the story."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Correct Course - Sprint Change Management Workflow
|
||||
name: "correct-course"
|
||||
description: "Navigate significant changes during sprint execution by analyzing impact, proposing solutions, and routing for implementation"
|
||||
author: "BMad Method"
|
||||
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
output_folder: "{config_source}:output_folder"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: create-story
|
||||
description: "Create the next user story markdown from epics/PRD and architecture, using a standard template and saving to the stories folder"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: dev-story
|
||||
description: "Execute a story by implementing tasks/subtasks, writing tests, validating, and updating the story file per acceptance criteria"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: epic-tech-context
|
||||
description: "Generate a comprehensive Technical Specification from PRD and Architecture with acceptance criteria and traceability mapping"
|
||||
author: "BMAD BMM"
|
||||
|
||||
# Critical variables
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Retrospective - Epic Completion Review Workflow
|
||||
name: "retrospective"
|
||||
description: "Run after epic completion to review overall success, extract lessons learned, and explore if new information emerged that might impact the next epic"
|
||||
author: "BMad"
|
||||
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
output_folder: "{config_source}:output_folder"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: sprint-planning
|
||||
description: "Generate and manage the sprint status tracking file for Phase 4 implementation, extracting all epics and stories from epic files and tracking their status through the development lifecycle"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Story Context Creation Workflow
|
||||
name: story-context
|
||||
description: "Assemble a dynamic Story Context XML by pulling latest documentation and existing code/library artifacts relevant to a drafted story"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Story Done Workflow (DEV Agent)
|
||||
name: story-done
|
||||
description: "Marks a story as done (DoD complete) and moves it from its current status → DONE in the status file. Advances the story queue. Simple status-update workflow with no searching required."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Story Ready Workflow (SM Agent)
|
||||
name: story-ready
|
||||
description: "Marks a drafted story as ready for development and moves it from TODO → IN PROGRESS in the status file. Simple status-update workflow with no searching required."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: create-excalidraw-dataflow
|
||||
description: "Create data flow diagrams (DFD) in Excalidraw format"
|
||||
author: "BMad"
|
||||
|
||||
# Config values
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: create-excalidraw-diagram
|
||||
description: "Create system architecture diagrams, ERDs, UML diagrams, or general technical diagrams in Excalidraw format"
|
||||
author: "BMad"
|
||||
|
||||
# Config values
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: create-excalidraw-flowchart
|
||||
description: "Create a flowchart visualization in Excalidraw format for processes, pipelines, or logic flows"
|
||||
author: "BMad"
|
||||
|
||||
# Config values
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
name: create-excalidraw-wireframe
|
||||
description: "Create website or app wireframes in Excalidraw format"
|
||||
author: "BMad"
|
||||
|
||||
# Config values
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
# Document Project Workflow Configuration
|
||||
name: "document-project"
|
||||
version: "1.2.0"
|
||||
description: "Analyzes and documents brownfield projects by scanning codebase, architecture, and patterns to create comprehensive reference documentation for AI-assisted development"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Test Architect workflow: atdd
|
||||
name: testarch-atdd
|
||||
description: "Generate failing acceptance tests before implementation using TDD red-green-refactor cycle"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Test Architect workflow: automate
|
||||
name: testarch-automate
|
||||
description: "Expand test automation coverage after implementation or analyze existing codebase to generate comprehensive test suite"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Test Architect workflow: ci
|
||||
name: testarch-ci
|
||||
description: "Scaffold CI/CD quality pipeline with test execution, burn-in loops, and artifact collection"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Test Architect workflow: framework
|
||||
name: testarch-framework
|
||||
description: "Initialize production-ready test framework architecture (Playwright or Cypress) with fixtures, helpers, and configuration"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Test Architect workflow: nfr-assess
|
||||
name: testarch-nfr
|
||||
description: "Assess non-functional requirements (performance, security, reliability, maintainability) before release with evidence-based validation"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Test Architect workflow: test-design
|
||||
name: testarch-test-design
|
||||
description: "Dual-mode workflow: (1) System-level testability review in Solutioning phase, or (2) Epic-level test planning in Implementation phase. Auto-detects mode based on project phase."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Test Architect workflow: test-review
|
||||
name: testarch-test-review
|
||||
description: "Review test quality using comprehensive knowledge base and best practices validation"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Test Architect workflow: trace (enhanced with gate decision)
|
||||
name: testarch-trace
|
||||
description: "Generate requirements-to-tests traceability matrix, analyze coverage, and make quality gate decision (PASS/CONCERNS/FAIL/WAIVED)"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Workflow Init - Initial Project Setup
|
||||
name: workflow-init
|
||||
description: "Initialize a new BMM project by determining level, type, and creating workflow path"
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Workflow Status - Master Router and Status Tracker
|
||||
name: workflow-status
|
||||
description: 'Lightweight status checker - answers "what should I do now?" for any agent. Reads YAML status file for workflow tracking. Use workflow-init for new projects.'
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables from config
|
||||
config_source: "{project-root}/{bmad_folder}/bmm/config.yaml"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Design Thinking Workflow Configuration
|
||||
name: "design-thinking"
|
||||
description: "Guide human-centered design processes using empathy-driven methodologies. This workflow walks through the design thinking phases - Empathize, Define, Ideate, Prototype, and Test - to create solutions deeply rooted in user needs."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/cis/config.yaml"
|
||||
|
|
@ -29,7 +28,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "design-thinking"
|
||||
description: "Guide human-centered design processes using empathy-driven methodologies. This workflow walks through the design thinking phases - Empathize, Define, Ideate, Prototype, and Test - to create solutions deeply rooted in user needs."
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/cis/workflows/design-thinking/instructions.md"
|
||||
template: "{bmad_folder}/cis/workflows/design-thinking/template.md"
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Innovation Strategy Workflow Configuration
|
||||
name: "innovation-strategy"
|
||||
description: "Identify disruption opportunities and architect business model innovation. This workflow guides strategic analysis of markets, competitive dynamics, and business model innovation to uncover sustainable competitive advantages and breakthrough opportunities."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/cis/config.yaml"
|
||||
|
|
@ -29,7 +28,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "innovation-strategy"
|
||||
description: "Identify disruption opportunities and architect business model innovation. This workflow guides strategic analysis of markets, competitive dynamics, and business model innovation to uncover sustainable competitive advantages and breakthrough opportunities."
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/cis/workflows/innovation-strategy/instructions.md"
|
||||
template: "{bmad_folder}/cis/workflows/innovation-strategy/template.md"
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Problem Solving Workflow Configuration
|
||||
name: "problem-solving"
|
||||
description: "Apply systematic problem-solving methodologies to crack complex challenges. This workflow guides through problem diagnosis, root cause analysis, creative solution generation, evaluation, and implementation planning using proven frameworks."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/cis/config.yaml"
|
||||
|
|
@ -29,7 +28,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "problem-solving"
|
||||
description: "Apply systematic problem-solving methodologies to crack complex challenges. This workflow guides through problem diagnosis, root cause analysis, creative solution generation, evaluation, and implementation planning using proven frameworks."
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/cis/workflows/problem-solving/instructions.md"
|
||||
template: "{bmad_folder}/cis/workflows/problem-solving/template.md"
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
# Storytelling Workflow Configuration
|
||||
name: "storytelling"
|
||||
description: "Craft compelling narratives using proven story frameworks and techniques. This workflow guides users through structured narrative development, applying appropriate story frameworks to create emotionally resonant and engaging stories for any purpose."
|
||||
author: "BMad"
|
||||
|
||||
# Critical variables load from config_source
|
||||
config_source: "{project-root}/{bmad_folder}/cis/config.yaml"
|
||||
|
|
@ -29,7 +28,6 @@ standalone: true
|
|||
web_bundle:
|
||||
name: "storytelling"
|
||||
description: "Craft compelling narratives using proven story frameworks and techniques. This workflow guides users through structured narrative development, applying appropriate story frameworks to create emotionally resonant and engaging stories for any purpose."
|
||||
author: "BMad"
|
||||
instructions: "{bmad_folder}/cis/workflows/storytelling/instructions.md"
|
||||
template: "{bmad_folder}/cis/workflows/storytelling/template.md"
|
||||
web_bundle_files:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# Expected: FAIL
|
||||
# Error code: custom
|
||||
# Error path: name
|
||||
# Error message: kebab-case
|
||||
|
||||
name: InvalidWorkflow_Name
|
||||
description: Workflow with invalid name format
|
||||
author: Test Author
|
||||
instructions: "{project-root}/path/to/instructions.md"
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Expected: FAIL
|
||||
# Error code: invalid_type
|
||||
# Error path: standalone
|
||||
# Error message: boolean
|
||||
|
||||
name: test-workflow
|
||||
description: Test workflow
|
||||
author: Test Author
|
||||
instructions: "{project-root}/path/to/instructions.md"
|
||||
standalone: "yes"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Expected: FAIL
|
||||
# Error code: invalid_union
|
||||
# Error message: Invalid input
|
||||
|
||||
name: test-workflow
|
||||
description: Test workflow
|
||||
instructions: "{project-root}/path/instructions.md"
|
||||
template: true
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Expected: FAIL
|
||||
# Error code: invalid_union
|
||||
# Error message: Invalid input
|
||||
|
||||
name: test-workflow
|
||||
description: Test workflow
|
||||
instructions: "{project-root}/path/instructions.md"
|
||||
web_bundle: true
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Expected: FAIL
|
||||
# Error code: custom
|
||||
# Error message: Malformed config reference
|
||||
|
||||
name: test-workflow
|
||||
description: Test workflow with malformed config reference
|
||||
author: Test Author
|
||||
instructions: "{project-root}/path/instructions.md"
|
||||
config_source: "{project-root}/.bmad/config.yaml"
|
||||
output_folder: "{config_source}output_folder"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Expected: FAIL
|
||||
# Error code: invalid_type
|
||||
# Error path: description
|
||||
# Error message: expected string, received undefined
|
||||
|
||||
name: test-workflow
|
||||
author: Test Author
|
||||
instructions: "{project-root}/path/to/instructions.md"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Expected: FAIL
|
||||
# Error code: invalid_type
|
||||
# Error path: instructions
|
||||
# Error message: expected string, received undefined
|
||||
|
||||
name: test-workflow
|
||||
description: Test workflow
|
||||
author: Test Author
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Expected: FAIL
|
||||
# Error code: invalid_type
|
||||
# Error path: name
|
||||
# Error message: expected string, received undefined
|
||||
|
||||
description: Missing name field
|
||||
author: Test Author
|
||||
instructions: "{project-root}/path/to/instructions.md"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# Expected: FAIL
|
||||
# Error code: custom
|
||||
# Error message: unclosed braces
|
||||
|
||||
name: test-workflow
|
||||
description: Test workflow with unclosed brace
|
||||
author: Test Author
|
||||
instructions: "{project-root}/path/instructions.md"
|
||||
output_folder: "{project-root/docs"
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Expected: FAIL
|
||||
# Error code: custom
|
||||
# Error message: Unknown config variable
|
||||
|
||||
name: test-workflow
|
||||
description: Test workflow with typo in config variable
|
||||
author: Test Author
|
||||
instructions: "{project-root}/path/instructions.md"
|
||||
config_source: "{project-root}/.bmad/config.yaml"
|
||||
output_folder: "{config_source}:ouput_folder"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Expected: FAIL
|
||||
# Error code: custom
|
||||
# Error message: Unknown path placeholder
|
||||
|
||||
name: test-workflow
|
||||
description: Test workflow with typo in placeholder
|
||||
author: Test Author
|
||||
instructions: "{project-rot}/path/instructions.md"
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# Expected: PASS
|
||||
# Valid complex workflow with all optional fields
|
||||
|
||||
name: complex-workflow
|
||||
description: A complex workflow with all features
|
||||
standalone: true
|
||||
template: "{installed_path}/template.md"
|
||||
instructions: "{installed_path}/instructions.md"
|
||||
validation: "{installed_path}/checklist.md"
|
||||
config_source: "{project-root}/.bmad/bmm/config.yaml"
|
||||
installed_path: "{project-root}/.bmad/bmm/workflows/complex-workflow"
|
||||
default_output_file: "{output_folder}/output.md"
|
||||
autonomous: false
|
||||
|
||||
input_file_patterns:
|
||||
architecture:
|
||||
description: "System architecture"
|
||||
whole: "{output_folder}/*architecture*.md"
|
||||
sharded: "{output_folder}/*architecture*/*.md"
|
||||
load_strategy: "FULL_LOAD"
|
||||
epics:
|
||||
description: "Epic files"
|
||||
sharded_single: "{output_folder}/*epic*/epic-{{epic_num}}.md"
|
||||
load_strategy: "SELECTIVE_LOAD"
|
||||
|
||||
exit_triggers:
|
||||
- "*exit"
|
||||
- "*quit"
|
||||
|
||||
web_bundle:
|
||||
name: "complex-workflow"
|
||||
description: "Web bundle for complex workflow"
|
||||
instructions: "{bmad_folder}/workflows/complex/instructions.md"
|
||||
web_bundle_files:
|
||||
- "{bmad_folder}/workflows/complex/instructions.md"
|
||||
- "{bmad_folder}/workflows/complex/template.md"
|
||||
|
||||
# Custom workflow variables
|
||||
output_folder: "{config_source}:output_folder"
|
||||
user_name: "{config_source}:user_name"
|
||||
date: system-generated
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Expected: PASS
|
||||
# Valid minimal workflow with only required fields
|
||||
|
||||
name: minimal-workflow
|
||||
description: A minimal valid workflow
|
||||
author: Test Author
|
||||
instructions: "{project-root}/path/to/instructions.md"
|
||||
|
|
@ -0,0 +1,301 @@
|
|||
/**
|
||||
* Workflow Schema Validation Test Runner
|
||||
*
|
||||
* Runs all test fixtures and verifies expected outcomes.
|
||||
* Reports pass/fail for each test and overall coverage statistics.
|
||||
*
|
||||
* Usage: node test/test-workflow-schema.js
|
||||
* Exit codes: 0 = all tests pass, 1 = test failures
|
||||
*
|
||||
* TODO: Extract shared test utilities (parseTestMetadata, validateError, parsePathString, colors)
|
||||
* into a common test helper module to reduce duplication with test-agent-schema.js
|
||||
*/
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const yaml = require('js-yaml');
|
||||
const { validateWorkflowFile } = require('../tools/schema/workflow.js');
|
||||
const { glob } = require('glob');
|
||||
|
||||
// ANSI color codes
|
||||
const colors = {
|
||||
reset: '\u001B[0m',
|
||||
green: '\u001B[32m',
|
||||
red: '\u001B[31m',
|
||||
yellow: '\u001B[33m',
|
||||
blue: '\u001B[34m',
|
||||
cyan: '\u001B[36m',
|
||||
dim: '\u001B[2m',
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse test metadata from YAML comments
|
||||
* @param {string} filePath
|
||||
* @returns {{shouldPass: boolean, errorExpectation?: object}}
|
||||
*/
|
||||
function parseTestMetadata(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
let shouldPass = true;
|
||||
const errorExpectation = {};
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.includes('Expected: PASS')) {
|
||||
shouldPass = true;
|
||||
} else if (line.includes('Expected: FAIL')) {
|
||||
shouldPass = false;
|
||||
}
|
||||
|
||||
// Parse error metadata
|
||||
const codeMatch = line.match(/^# Error code: (.+)$/);
|
||||
if (codeMatch) {
|
||||
errorExpectation.code = codeMatch[1].trim();
|
||||
}
|
||||
|
||||
const pathMatch = line.match(/^# Error path: (.+)$/);
|
||||
if (pathMatch) {
|
||||
errorExpectation.path = pathMatch[1].trim();
|
||||
}
|
||||
|
||||
const messageMatch = line.match(/^# Error message: (.+)$/);
|
||||
if (messageMatch) {
|
||||
errorExpectation.message = messageMatch[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
shouldPass,
|
||||
errorExpectation: Object.keys(errorExpectation).length > 0 ? errorExpectation : null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert dot-notation path string to array
|
||||
* e.g., "name" => ["name"]
|
||||
*/
|
||||
function parsePathString(pathString) {
|
||||
return pathString
|
||||
.replaceAll(/\[(\d+)\]/g, '.$1')
|
||||
.split('.')
|
||||
.map((part) => {
|
||||
const num = parseInt(part, 10);
|
||||
return isNaN(num) ? part : num;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate error against expectations
|
||||
* @param {object} error - Zod error issue
|
||||
* @param {object} expectation - Expected error structure
|
||||
* @returns {{valid: boolean, reason?: string}}
|
||||
*/
|
||||
function validateError(error, expectation) {
|
||||
if (expectation.code && error.code !== expectation.code) {
|
||||
return { valid: false, reason: `Expected code "${expectation.code}", got "${error.code}"` };
|
||||
}
|
||||
|
||||
if (expectation.path) {
|
||||
const expectedPath = parsePathString(expectation.path);
|
||||
const actualPath = error.path;
|
||||
|
||||
if (JSON.stringify(expectedPath) !== JSON.stringify(actualPath)) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: `Expected path ${JSON.stringify(expectedPath)}, got ${JSON.stringify(actualPath)}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (expectation.message && !error.message.includes(expectation.message)) {
|
||||
return {
|
||||
valid: false,
|
||||
reason: `Expected message to include "${expectation.message}", got "${error.message}"`,
|
||||
};
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a single test case
|
||||
* @param {string} filePath
|
||||
* @returns {{passed: boolean, message: string}}
|
||||
*/
|
||||
function runTest(filePath) {
|
||||
const metadata = parseTestMetadata(filePath);
|
||||
const { shouldPass, errorExpectation } = metadata;
|
||||
|
||||
try {
|
||||
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
let workflowData;
|
||||
|
||||
try {
|
||||
workflowData = yaml.load(fileContent);
|
||||
} catch (parseError) {
|
||||
if (shouldPass) {
|
||||
return {
|
||||
passed: false,
|
||||
message: `Expected PASS but got YAML parse error: ${parseError.message}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
passed: true,
|
||||
message: 'Got expected YAML parse error',
|
||||
};
|
||||
}
|
||||
|
||||
const result = validateWorkflowFile(filePath, workflowData);
|
||||
|
||||
if (result.success && shouldPass) {
|
||||
return {
|
||||
passed: true,
|
||||
message: 'Validation passed as expected',
|
||||
};
|
||||
}
|
||||
|
||||
if (!result.success && !shouldPass) {
|
||||
const actualError = result.error.issues[0];
|
||||
|
||||
if (errorExpectation) {
|
||||
const validation = validateError(actualError, errorExpectation);
|
||||
|
||||
if (!validation.valid) {
|
||||
return {
|
||||
passed: false,
|
||||
message: `Error validation failed: ${validation.reason}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
passed: true,
|
||||
message: `Got expected error (${errorExpectation.code}): ${actualError.message}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
passed: true,
|
||||
message: `Got expected validation error: ${actualError?.message}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (result.success && !shouldPass) {
|
||||
return {
|
||||
passed: false,
|
||||
message: 'Expected validation to FAIL but it PASSED',
|
||||
};
|
||||
}
|
||||
|
||||
if (!result.success && shouldPass) {
|
||||
return {
|
||||
passed: false,
|
||||
message: `Expected validation to PASS but it FAILED: ${result.error.issues[0]?.message}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
passed: false,
|
||||
message: 'Unexpected test state',
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
passed: false,
|
||||
message: `Test execution error: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main test runner
|
||||
*/
|
||||
async function main() {
|
||||
console.log(`${colors.cyan}╔═══════════════════════════════════════════════════════════╗${colors.reset}`);
|
||||
console.log(`${colors.cyan}║ Workflow Schema Validation Test Suite ║${colors.reset}`);
|
||||
console.log(`${colors.cyan}╚═══════════════════════════════════════════════════════════╝${colors.reset}\n`);
|
||||
|
||||
// Find all test fixtures
|
||||
const testFiles = await glob('test/fixtures/workflow-schema/**/*.workflow.yaml', {
|
||||
cwd: path.join(__dirname, '..'),
|
||||
absolute: true,
|
||||
});
|
||||
|
||||
if (testFiles.length === 0) {
|
||||
console.log(`${colors.yellow}⚠️ No test fixtures found${colors.reset}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`Found ${colors.cyan}${testFiles.length}${colors.reset} test fixture(s)\n`);
|
||||
|
||||
// Group tests by category
|
||||
const categories = {};
|
||||
for (const testFile of testFiles) {
|
||||
const relativePath = path.relative(path.join(__dirname, 'fixtures/workflow-schema'), testFile);
|
||||
const parts = relativePath.split(path.sep);
|
||||
const validInvalid = parts[0]; // 'valid' or 'invalid'
|
||||
const category = parts[1] || 'general'; // category folder or 'general'
|
||||
|
||||
const categoryKey = `${validInvalid}/${category}`;
|
||||
if (!categories[categoryKey]) {
|
||||
categories[categoryKey] = [];
|
||||
}
|
||||
categories[categoryKey].push(testFile);
|
||||
}
|
||||
|
||||
// Run tests by category
|
||||
let totalTests = 0;
|
||||
let passedTests = 0;
|
||||
const failures = [];
|
||||
|
||||
for (const [categoryKey, files] of Object.entries(categories).sort()) {
|
||||
const [validInvalid, category] = categoryKey.split('/');
|
||||
const categoryLabel = category.replaceAll('-', ' ').toUpperCase();
|
||||
const validLabel = validInvalid === 'valid' ? '✅' : '❌';
|
||||
|
||||
console.log(`${colors.blue}${validLabel} ${categoryLabel} (${validInvalid})${colors.reset}`);
|
||||
|
||||
for (const testFile of files) {
|
||||
totalTests++;
|
||||
const testName = path.basename(testFile, '.workflow.yaml');
|
||||
const result = runTest(testFile);
|
||||
|
||||
if (result.passed) {
|
||||
passedTests++;
|
||||
console.log(` ${colors.green}✓${colors.reset} ${testName} ${colors.dim}${result.message}${colors.reset}`);
|
||||
} else {
|
||||
console.log(` ${colors.red}✗${colors.reset} ${testName} ${colors.red}${result.message}${colors.reset}`);
|
||||
failures.push({
|
||||
file: path.relative(process.cwd(), testFile),
|
||||
message: result.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Summary
|
||||
console.log(`${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
|
||||
console.log(`${colors.cyan}Test Results:${colors.reset}`);
|
||||
console.log(` Total: ${totalTests}`);
|
||||
console.log(` Passed: ${colors.green}${passedTests}${colors.reset}`);
|
||||
console.log(` Failed: ${passedTests === totalTests ? colors.green : colors.red}${totalTests - passedTests}${colors.reset}`);
|
||||
console.log(`${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}\n`);
|
||||
|
||||
// Report failures
|
||||
if (failures.length > 0) {
|
||||
console.log(`${colors.red}❌ FAILED TESTS:${colors.reset}\n`);
|
||||
for (const failure of failures) {
|
||||
console.log(`${colors.red}✗${colors.reset} ${failure.file}`);
|
||||
console.log(` ${failure.message}\n`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`${colors.green}✨ All tests passed!${colors.reset}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Run
|
||||
main().catch((error) => {
|
||||
console.error(`${colors.red}Fatal error:${colors.reset}`, error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -127,8 +127,7 @@ class WorkflowCommandGenerator {
|
|||
.replaceAll('{{description}}', workflow.description)
|
||||
.replaceAll('{{workflow_path}}', workflowPath)
|
||||
.replaceAll('{bmad_folder}', this.bmadFolderName)
|
||||
.replaceAll('{{interactive}}', workflow.interactive)
|
||||
.replaceAll('{{author}}', workflow.author || 'BMAD');
|
||||
.replaceAll('{{interactive}}', workflow.interactive);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,449 @@
|
|||
// Zod schema definition for workflow.yaml files
|
||||
const { z } = require('zod');
|
||||
|
||||
const KEBAB_CASE_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
||||
|
||||
// Public API ---------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validate a workflow YAML payload against the schema.
|
||||
* Exposed as the single public entry point, so callers do not reach into schema internals.
|
||||
*
|
||||
* @param {string} filePath Path to the workflow file (for error reporting).
|
||||
* @param {unknown} workflowYaml Parsed YAML content.
|
||||
* @returns {object} Result with success, data/error, and warnings array
|
||||
*/
|
||||
function validateWorkflowFile(filePath, workflowYaml) {
|
||||
const schema = workflowSchema();
|
||||
const result = schema.safeParse(workflowYaml);
|
||||
const warnings = collectUnknownFieldWarnings(workflowYaml);
|
||||
return { ...result, warnings };
|
||||
}
|
||||
|
||||
module.exports = { validateWorkflowFile };
|
||||
|
||||
// Canonical lists of known variable references (extracted from 65 real workflows)
|
||||
// These are used to validate variable references and catch typos
|
||||
|
||||
const KNOWN_PATH_PLACEHOLDERS = [
|
||||
'agent_filename',
|
||||
'bmad_folder',
|
||||
'custom_agent_location',
|
||||
'custom_module_location',
|
||||
'custom_workflow_location',
|
||||
'date',
|
||||
'epic_id',
|
||||
'epic_num',
|
||||
'game_name',
|
||||
'installed_path',
|
||||
'item_name',
|
||||
'item_type',
|
||||
'module_code',
|
||||
'output_folder',
|
||||
'path',
|
||||
'prev_epic_num',
|
||||
'project-root',
|
||||
'project_name',
|
||||
'reference_agents',
|
||||
'research_type',
|
||||
'shared_path',
|
||||
'sprint_artifacts',
|
||||
'status_file',
|
||||
'story_dir',
|
||||
'story_id',
|
||||
'story_key',
|
||||
'story_path',
|
||||
'target_module',
|
||||
'test_dir',
|
||||
'timestamp',
|
||||
'workflow_name',
|
||||
'workflow_template_path',
|
||||
];
|
||||
|
||||
const KNOWN_CONFIG_VARIABLES = [
|
||||
'communication_language',
|
||||
'custom_agent_location',
|
||||
'custom_module_location',
|
||||
'custom_workflow_location',
|
||||
'document_output_language',
|
||||
'game_dev_experience',
|
||||
'output_folder',
|
||||
'project_name',
|
||||
'sprint_artifacts',
|
||||
'user_name',
|
||||
'user_skill_level',
|
||||
];
|
||||
|
||||
const KNOWN_TEMPLATE_VARIABLES = [
|
||||
'agent_filename',
|
||||
'date',
|
||||
'epic_id',
|
||||
'epic_num',
|
||||
'game_name',
|
||||
'item_name',
|
||||
'item_type',
|
||||
'module_code',
|
||||
'prev_epic_num',
|
||||
'project_name',
|
||||
'research_type',
|
||||
'story_key',
|
||||
'target_module',
|
||||
'workflow_name',
|
||||
];
|
||||
|
||||
// Internal helpers ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Build a Zod schema for validating a single workflow definition.
|
||||
*
|
||||
* @returns {import('zod').ZodSchema} Configured Zod schema instance.
|
||||
*/
|
||||
function workflowSchema() {
|
||||
return z
|
||||
.object({
|
||||
name: createKebabCaseString('name'),
|
||||
description: createNonEmptyString('description'),
|
||||
standalone: z.boolean().optional(),
|
||||
template: z.union([z.literal(false), z.string()]).optional(),
|
||||
instructions: createNonEmptyString('instructions'),
|
||||
validation: z.string().optional(),
|
||||
config_source: z.string().optional(),
|
||||
installed_path: z.string().optional(),
|
||||
output_folder: z.string().optional(),
|
||||
user_name: z.string().optional(),
|
||||
communication_language: z.string().optional(),
|
||||
date: z.string().optional(),
|
||||
brain_techniques: z.string().optional(),
|
||||
validation_output_file: z.string().optional(),
|
||||
required_tools: z.array(z.union([z.string(), z.object({ description: z.string().optional() }).catchall(z.unknown())])).optional(),
|
||||
default_output_file: z.string().optional(),
|
||||
autonomous: z.boolean().optional(),
|
||||
web_bundle: z.union([z.literal(false), buildWebBundleSchema()]).optional(),
|
||||
input_file_patterns: z.record(z.string(), buildInputFilePatternSchema()).optional(),
|
||||
exit_triggers: z.array(createNonEmptyString('exit_triggers[]')).optional(),
|
||||
|
||||
// Common optional fields (often injected variables or context)
|
||||
document_output_language: z.string().optional(),
|
||||
user_skill_level: z.string().optional(),
|
||||
sprint_artifacts: z.string().optional(),
|
||||
sprint_status: z.string().optional(),
|
||||
game_dev_experience: z.string().optional(),
|
||||
variables: z.record(z.string(), z.unknown()).optional(),
|
||||
checklist: z.string().optional(),
|
||||
story_dir: z.string().optional(),
|
||||
story_file: z.string().optional(),
|
||||
context_file: z.string().optional(),
|
||||
game_types_csv: z.string().optional(),
|
||||
game_type_guides: z.string().optional(),
|
||||
game_context: z.string().optional(),
|
||||
game_brain_methods: z.string().optional(),
|
||||
core_brainstorming: z.string().optional(),
|
||||
decision_catalog: z.string().optional(),
|
||||
architecture_patterns: z.string().optional(),
|
||||
pattern_categories: z.string().optional(),
|
||||
replaces: z.string().optional(),
|
||||
paradigm: z.string().optional(),
|
||||
execution_time: z.string().optional(),
|
||||
features: z.array(z.string()).optional(),
|
||||
project_context: z.string().optional(),
|
||||
|
||||
// Additional common fields
|
||||
tags: z.array(z.string()).optional(),
|
||||
execution_hints: z.record(z.string(), z.unknown()).optional(),
|
||||
project_name: z.string().optional(),
|
||||
library: z.string().optional(),
|
||||
helpers: z.string().optional(),
|
||||
templates: z.string().optional(),
|
||||
json_validation: z.string().optional(),
|
||||
shared_path: z.string().optional(),
|
||||
})
|
||||
.catchall(z.unknown()) // Allow unknown fields for dynamic workflow variables
|
||||
.superRefine((data, ctx) => {
|
||||
// Validate variable references in all string values
|
||||
validateVariableReferences(data, [], ctx);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema for web_bundle object when it's not a boolean.
|
||||
*/
|
||||
function buildWebBundleSchema() {
|
||||
return z
|
||||
.object({
|
||||
name: createNonEmptyString('web_bundle.name').optional(),
|
||||
description: createNonEmptyString('web_bundle.description').optional(),
|
||||
instructions: createNonEmptyString('web_bundle.instructions').optional(),
|
||||
web_bundle_files: z.array(createNonEmptyString('web_bundle.web_bundle_files[]')).optional(),
|
||||
validation: z.string().optional(),
|
||||
template: z.union([z.literal(false), z.string()]).optional(),
|
||||
existing_workflows: z.array(z.unknown()).optional(),
|
||||
})
|
||||
.catchall(z.unknown()); // Allow custom fields like agent_manifest, author, etc.
|
||||
}
|
||||
|
||||
/**
|
||||
* Schema for individual input_file_patterns entries.
|
||||
*/
|
||||
function buildInputFilePatternSchema() {
|
||||
return z
|
||||
.object({
|
||||
description: createNonEmptyString('input_file_patterns[].description').optional(),
|
||||
whole: z.string().optional(),
|
||||
sharded: z.string().optional(),
|
||||
sharded_index: z.string().optional(),
|
||||
sharded_single: z.string().optional(),
|
||||
pattern: z.string().optional(), // Legacy support
|
||||
load_strategy: z.enum(['FULL_LOAD', 'SELECTIVE_LOAD', 'INDEX_GUIDED']).optional(),
|
||||
})
|
||||
.strict(); // Warn about unknown fields - patterns have a defined schema
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively validate variable references in all string values.
|
||||
* Checks for:
|
||||
* 1. Malformed syntax (unclosed braces, invalid patterns)
|
||||
* 2. Unknown placeholders/variables (warns about potential typos)
|
||||
*
|
||||
* @param {any} obj - Object to validate
|
||||
* @param {Array} path - Current path in object tree
|
||||
* @param {import('zod').RefinementCtx} ctx - Zod context for adding issues
|
||||
*/
|
||||
function collectUnknownFieldWarnings(workflowYaml) {
|
||||
const warnings = [];
|
||||
if (typeof workflowYaml === 'object' && workflowYaml !== null) {
|
||||
const knownFields = new Set([
|
||||
'name',
|
||||
'description',
|
||||
'standalone',
|
||||
'template',
|
||||
'instructions',
|
||||
'validation',
|
||||
'config_source',
|
||||
'installed_path',
|
||||
'output_folder',
|
||||
'user_name',
|
||||
'communication_language',
|
||||
'date',
|
||||
'brain_techniques',
|
||||
'validation_output_file',
|
||||
'required_tools',
|
||||
'default_output_file',
|
||||
'autonomous',
|
||||
'web_bundle',
|
||||
'input_file_patterns',
|
||||
'exit_triggers',
|
||||
// Added common fields
|
||||
'document_output_language',
|
||||
'user_skill_level',
|
||||
'sprint_artifacts',
|
||||
'sprint_status',
|
||||
'game_dev_experience',
|
||||
'variables',
|
||||
'checklist',
|
||||
'story_dir',
|
||||
'story_file',
|
||||
'context_file',
|
||||
'game_types_csv',
|
||||
'game_type_guides',
|
||||
'game_context',
|
||||
'game_brain_methods',
|
||||
'core_brainstorming',
|
||||
'decision_catalog',
|
||||
'architecture_patterns',
|
||||
'pattern_categories',
|
||||
'replaces',
|
||||
'paradigm',
|
||||
'execution_time',
|
||||
'features',
|
||||
'project_context',
|
||||
// Additional common fields
|
||||
'tags',
|
||||
'execution_hints',
|
||||
'project_name',
|
||||
'library',
|
||||
'helpers',
|
||||
'templates',
|
||||
'json_validation',
|
||||
'shared_path',
|
||||
]);
|
||||
for (const key of Object.keys(workflowYaml)) {
|
||||
if (!knownFields.has(key)) {
|
||||
warnings.push({
|
||||
path: [key],
|
||||
message: `Unknown field '${key}' - not in workflow schema. This may be a workflow-specific variable or a typo.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
// web_bundle unknown fields
|
||||
if (workflowYaml.web_bundle && typeof workflowYaml.web_bundle === 'object') {
|
||||
const knownWebBundleFields = new Set([
|
||||
'name',
|
||||
'description',
|
||||
'instructions',
|
||||
'web_bundle_files',
|
||||
'validation',
|
||||
'template',
|
||||
'existing_workflows',
|
||||
]);
|
||||
for (const key of Object.keys(workflowYaml.web_bundle)) {
|
||||
if (!knownWebBundleFields.has(key)) {
|
||||
warnings.push({
|
||||
path: ['web_bundle', key],
|
||||
message: `Unknown field 'web_bundle.${key}' - not in web_bundle schema. This may be a custom field or a typo.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// input_file_patterns unknown fields
|
||||
if (workflowYaml.input_file_patterns && typeof workflowYaml.input_file_patterns === 'object') {
|
||||
const knownPatternFields = new Set([
|
||||
'description',
|
||||
'whole',
|
||||
'sharded',
|
||||
'sharded_index',
|
||||
'sharded_single',
|
||||
'load_strategy',
|
||||
'pattern',
|
||||
]);
|
||||
for (const [patternName, pattern] of Object.entries(workflowYaml.input_file_patterns)) {
|
||||
if (typeof pattern === 'object' && pattern !== null) {
|
||||
for (const key of Object.keys(pattern)) {
|
||||
if (!knownPatternFields.has(key)) {
|
||||
warnings.push({
|
||||
path: ['input_file_patterns', patternName, key],
|
||||
message: `Unknown field 'input_file_patterns.${patternName}.${key}' - not in pattern schema. This may be a typo.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively validate variable references in all string values.
|
||||
*
|
||||
* @param {any} data - Data to validate
|
||||
* @param {Array} path - Current path
|
||||
* @param {import('zod').RefinementCtx} ctx - Zod context
|
||||
*/
|
||||
function validateVariableReferences(data, path, ctx) {
|
||||
if (typeof data === 'string') {
|
||||
validateStringReferences(data, path, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
for (const [index, item] of data.entries()) {
|
||||
validateVariableReferences(item, [...path, index], ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
validateVariableReferences(value, [...path, key], ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate variable references in a single string value.
|
||||
*/
|
||||
function validateStringReferences(str, path, ctx) {
|
||||
// Check for unclosed braces
|
||||
const openBraces = (str.match(/\{/g) || []).length;
|
||||
const closeBraces = (str.match(/\}/g) || []).length;
|
||||
if (openBraces !== closeBraces) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
path,
|
||||
message: `Malformed variable reference: unclosed braces (${openBraces} open, ${closeBraces} close)`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract and validate path placeholders: {placeholder}
|
||||
const pathPlaceholderMatches = str.matchAll(/\{([a-z][a-z0-9_-]*)\}/g);
|
||||
for (const match of pathPlaceholderMatches) {
|
||||
const placeholder = match[1];
|
||||
// Skip config_source as it's a special keyword
|
||||
if (placeholder === 'config_source') continue;
|
||||
|
||||
if (!KNOWN_PATH_PLACEHOLDERS.includes(placeholder)) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
path,
|
||||
message: `Unknown path placeholder: {${placeholder}} - possible typo? Known placeholders: ${KNOWN_PATH_PLACEHOLDERS.slice(0, 5).join(', ')}...`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Extract and validate config variables: {config_source}:variable
|
||||
const configMatches = str.matchAll(/\{config_source\}:([a-z][a-z0-9_]*)/g);
|
||||
for (const match of configMatches) {
|
||||
const variable = match[1];
|
||||
if (!KNOWN_CONFIG_VARIABLES.includes(variable)) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
path,
|
||||
message: `Unknown config variable: {config_source}:${variable} - possible typo? Known variables: ${KNOWN_CONFIG_VARIABLES.slice(0, 5).join(', ')}...`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Extract and validate template variables: {{variable}}
|
||||
const templateMatches = str.matchAll(/\{\{([a-z][a-z0-9_]*)\}\}/g);
|
||||
for (const match of templateMatches) {
|
||||
const variable = match[1];
|
||||
if (!KNOWN_TEMPLATE_VARIABLES.includes(variable)) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
path,
|
||||
message: `Unknown template variable: {{${variable}}} - possible typo? Known variables: ${KNOWN_TEMPLATE_VARIABLES.slice(0, 5).join(', ')}...`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for malformed config_source references (missing colon)
|
||||
if (str.includes('{config_source}') && !/\{config_source\}:[a-z]/.test(str)) {
|
||||
const malformedMatch = str.match(/\{config_source\}([^:]|$)/);
|
||||
if (malformedMatch) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
path,
|
||||
message: 'Malformed config reference: {config_source} must be followed by :variable_name',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Primitive validators -----------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a validator for non-empty strings.
|
||||
* @param {string} label Field label for error messages.
|
||||
*/
|
||||
function createNonEmptyString(label) {
|
||||
return z.string().refine((value) => value.trim().length > 0, {
|
||||
message: `${label} must be a non-empty string`,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a validator for kebab-case strings.
|
||||
* @param {string} label Field label for error messages.
|
||||
*/
|
||||
function createKebabCaseString(label) {
|
||||
return z.string().refine(
|
||||
(value) => {
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 && KEBAB_CASE_PATTERN.test(trimmed);
|
||||
},
|
||||
{
|
||||
message: `${label} must be kebab-case (lowercase words separated by hyphen)`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
* Workflow Schema Validator CLI
|
||||
*
|
||||
* Scans all *.workflow.yaml files in src/
|
||||
* and validates them against the Zod schema.
|
||||
*
|
||||
* Usage: node tools/validate-workflow-schema.js [project_root]
|
||||
* Exit codes: 0 = success, 1 = validation failures
|
||||
*
|
||||
* Optional argument:
|
||||
* project_root - Directory to scan (defaults to BMAD repo root)
|
||||
*/
|
||||
|
||||
const { glob } = require('glob');
|
||||
const yaml = require('js-yaml');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { validateWorkflowFile } = require('./schema/workflow.js');
|
||||
|
||||
/**
|
||||
* Main validation routine
|
||||
* @param {string} [customProjectRoot] - Optional project root to scan (for testing)
|
||||
*/
|
||||
async function main(customProjectRoot) {
|
||||
console.log('🔍 Scanning for workflow files...\n');
|
||||
|
||||
// Determine project root: use custom path if provided, otherwise default to repo root
|
||||
const project_root = customProjectRoot || path.join(__dirname, '..');
|
||||
|
||||
// Find all workflow files
|
||||
const workflowFiles = await glob('src/**/workflow.yaml', {
|
||||
cwd: project_root,
|
||||
absolute: true,
|
||||
ignore: ['**/node_modules/**', '**/workflow-template/**'], // Exclude templates and node_modules
|
||||
});
|
||||
|
||||
if (workflowFiles.length === 0) {
|
||||
console.log('❌ No workflow files found. This likely indicates a configuration error.');
|
||||
console.log(' Expected to find workflow.yaml files in src/');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Found ${workflowFiles.length} workflow file(s)\n`);
|
||||
|
||||
const errors = [];
|
||||
const warnings = [];
|
||||
|
||||
// Validate each file
|
||||
for (const filePath of workflowFiles) {
|
||||
const relativePath = path.relative(process.cwd(), filePath);
|
||||
|
||||
try {
|
||||
const fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
const workflowData = yaml.load(fileContent);
|
||||
|
||||
const result = validateWorkflowFile(relativePath, workflowData);
|
||||
|
||||
if (result.success) {
|
||||
// Filter out "Unknown field" warnings from the per-file output
|
||||
const relevantWarnings = (result.warnings || []).filter((w) => !w.message.startsWith('Unknown field'));
|
||||
|
||||
if (relevantWarnings.length > 0) {
|
||||
console.log(`⚠️ ${relativePath} (passed with warnings)`);
|
||||
warnings.push({
|
||||
file: relativePath,
|
||||
issues: relevantWarnings,
|
||||
});
|
||||
} else {
|
||||
console.log(`✅ ${relativePath}`);
|
||||
}
|
||||
|
||||
// Still collect ALL warnings (including unknown fields) for the summary
|
||||
if (result.warnings && result.warnings.length > 0) {
|
||||
const unknownFieldWarnings = result.warnings.filter((w) => w.message.startsWith('Unknown field'));
|
||||
if (unknownFieldWarnings.length > 0) {
|
||||
warnings.push({
|
||||
file: relativePath,
|
||||
issues: unknownFieldWarnings,
|
||||
silent: true, // Mark as silent so we don't double-print in the loop below if mixed
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errors.push({
|
||||
file: relativePath,
|
||||
issues: result.error.issues,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
errors.push({
|
||||
file: relativePath,
|
||||
issues: [
|
||||
{
|
||||
code: 'parse_error',
|
||||
message: `Failed to parse YAML: ${error.message}`,
|
||||
path: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Report warnings
|
||||
if (warnings.length > 0) {
|
||||
const unknownFields = new Map();
|
||||
|
||||
for (const { file, issues } of warnings) {
|
||||
for (const issue of issues) {
|
||||
const pathString = issue.path.length > 0 ? issue.path.join('.') : '(root)';
|
||||
// Only aggregate "Unknown field" warnings
|
||||
if (issue.message.startsWith('Unknown field')) {
|
||||
if (!unknownFields.has(pathString)) {
|
||||
unknownFields.set(pathString, []);
|
||||
}
|
||||
unknownFields.get(pathString).push(file);
|
||||
} else {
|
||||
// Print non-unknown-field warnings immediately (e.g. malformed vars)
|
||||
console.log(`\n⚠️ Warning in ${file}:`);
|
||||
console.log(` Path: ${pathString}`);
|
||||
console.log(` Message: ${issue.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unknownFields.size > 0) {
|
||||
console.log('\nℹ️ Unknown Fields Summary (not in schema, possibly custom variables):');
|
||||
const sortedFields = [...unknownFields.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
||||
|
||||
for (const [field, files] of sortedFields) {
|
||||
console.log(` - ${field} (${files.length} files)`);
|
||||
}
|
||||
console.log('\n (Run with --verbose to see specific files for each field)');
|
||||
}
|
||||
}
|
||||
|
||||
// Report errors
|
||||
if (errors.length > 0) {
|
||||
console.log('\n❌ Validation failed for the following files:\n');
|
||||
|
||||
for (const { file, issues } of errors) {
|
||||
console.log(`\n📄 ${file}`);
|
||||
for (const issue of issues) {
|
||||
const pathString = issue.path.length > 0 ? issue.path.join('.') : '(root)';
|
||||
console.log(` Path: ${pathString}`);
|
||||
console.log(` Error: ${issue.message}`);
|
||||
if (issue.code) {
|
||||
console.log(` Code: ${issue.code}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n\n💥 ${errors.length} file(s) failed validation`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`\n✨ All ${workflowFiles.length} workflow file(s) passed validation!\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Run with optional command-line argument for project root
|
||||
const customProjectRoot = process.argv[2];
|
||||
main(customProjectRoot).catch((error) => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
Reference in New Issue