From e0cfff50dae10f2eabc2247707d0b3726cc44953 Mon Sep 17 00:00:00 2001 From: Dicky Moore Date: Sun, 8 Feb 2026 15:52:47 +0000 Subject: [PATCH] fix: tighten workflow routing and validation consistency --- .../steps/step-04-decisions.md | 12 +- .../create-architecture/workflow.md | 2 +- .../steps/step-01-validate-prerequisites.md | 9 +- .../document-project/instructions.md | 113 ++++++++++++------ test/test-installation-components.js | 17 +-- 5 files changed, 103 insertions(+), 50 deletions(-) diff --git a/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md b/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md index 43b4efd9c..7828d0804 100644 --- a/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +++ b/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md @@ -15,9 +15,9 @@ ## EXECUTION PROTOCOLS: -- 🎯 Show your analysis before taking any action +- 🎯 Provide a brief rationale before taking any action - 🌐 Search the web to verify technology versions and options -- ⚠️ Present A/P/C menu after each major decision category +- ⚠️ Present A/P/C menu after drafting decision content (and when user requests refinement) - 💾 ONLY save when user chooses C (Continue) - 📖 Update frontmatter `stepsCompleted: [1, 2, 3, 4]` before loading next step - 🚫 FORBIDDEN to load next step until C is selected @@ -44,7 +44,7 @@ This step will generate content and present choices for each decision category: - Project context file may contain technical preferences and rules - Technical preferences discovered in step 3 are available - Focus on decisions not already made by starter template or existing preferences -- Collaborative decision making, not recommendations +- Collaborative decision making first; recommendations are allowed only with explicit rationale and user confirmation ## YOUR TASK: @@ -58,7 +58,7 @@ Facilitate collaborative architectural decision making, leveraging existing tech "Based on our technical preferences discussion in step 3, let's build on those foundations: **Your Technical Preferences:** -{{user_technical_preferences_from_step_3}} +{{user_technical_preferences}} **Starter Template Decisions:** {{starter_template_decisions}} @@ -72,7 +72,7 @@ Based on technical preferences, starter template choice, and project context, id **Already Decided (Don't re-decide these):** - {{starter_template_decisions}} -- {{user_technology_preferences}} +- {{user_technical_preferences}} - {{project_context_technical_rules}} **Critical Decisions:** Must be decided before implementation can proceed @@ -165,7 +165,7 @@ If decision involves specific technology: ``` Search the web: "{{technology}} latest stable version" -Search the web: "{{technology}} current LTS version" +Search the web: "{{technology}} current LTS version" (only if the technology publishes an LTS track) Search the web: "{{technology}} production readiness" ``` diff --git a/src/bmm/workflows/3-solutioning/create-architecture/workflow.md b/src/bmm/workflows/3-solutioning/create-architecture/workflow.md index b75b4a46c..cd4201650 100644 --- a/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +++ b/src/bmm/workflows/3-solutioning/create-architecture/workflow.md @@ -36,7 +36,7 @@ Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: ### Paths -- `installed_path` = `{project-root}/_bmad/bmm/workflows/3-solutioning/architecture` +- `installed_path` = `{project-root}/_bmad/bmm/workflows/3-solutioning/create-architecture` - `template_path` = `{installed_path}/architecture-decision-template.md` - `data_files_path` = `{installed_path}/data/` diff --git a/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md b/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md index d6eaef376..2dd277ee9 100644 --- a/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +++ b/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md @@ -88,11 +88,18 @@ Search for required documents using these patterns (sharded means a large docume 1. `{planning_artifacts}/*ux*.md` (whole document) 2. `{planning_artifacts}/*ux*/index.md` (sharded version) +**Deterministic Selection Rules (required):** + +- When multiple files match the same priority level, prefer exact filename matches (`prd.md`, `architecture.md`, `ux-design.md`) before wildcard matches. +- If multiple wildcard matches remain, prefer files under `{planning_artifacts}` root before nested paths. +- If ambiguity still remains, present candidates to the user and require explicit selection before extraction. +- Record the final selected files in `inputDocuments` frontmatter to keep downstream steps deterministic. + Before proceeding, Ask the user if there are any other documents to include for analysis, and if anything found should be excluded. Wait for user confirmation. Once confirmed, create the {outputFile} from the {epicsTemplate} and in the front matter list the files in the array of `inputDocuments: []`. ### 3. Extract Functional Requirements (FRs) -From the PRD document (full or sharded), read then entire document and extract ALL functional requirements: +From the PRD document (full or sharded), read the entire document and extract ALL functional requirements: **Extraction Method:** diff --git a/src/bmm/workflows/document-project/instructions.md b/src/bmm/workflows/document-project/instructions.md index 781fde439..f04f2aa89 100644 --- a/src/bmm/workflows/document-project/instructions.md +++ b/src/bmm/workflows/document-project/instructions.md @@ -10,10 +10,26 @@ - - mode: data - data_request: project_config - +Initialize status defaults: + - Set status_exists = false + - Set status_file_found = false + - Set standalone_mode = true + - Set warning = "" + - Set suggestion = "" + - Set next_workflow = "" + - Set next_agent = "" + + +Attempt to load workflow status directly from `{output_folder}/bmm-workflow-status.yaml`: + - If file exists, is readable, and parses correctly: + - Set status_exists = true + - Set status_file_found = true + - Set standalone_mode = false + - Set status_file_path = `{output_folder}/bmm-workflow-status.yaml` + - Extract field_type, warning, suggestion, next_workflow, next_agent if present + - If file is missing, unreadable, or malformed: + - Keep defaults and continue in standalone mode + {{suggestion}} @@ -35,11 +51,11 @@ - - - mode: validate - calling_workflow: document-project - + + Validate sequencing locally from loaded status fields: + - If warning is empty, continue + - If warning contains guidance, require explicit user confirmation before continuing + {{warning}} @@ -58,12 +74,23 @@ SMART LOADING STRATEGY: Check state file FIRST before loading any CSV files Check for existing state file at: {output_folder}/project-scan-report.json +Set resume_mode = false Read state file and extract: timestamps, mode, scan_level, current_step, completed_steps, project_classification Extract cached project_type_id(s) from state file if present Calculate age of state file (current time - last_updated) + + Display: "Found old state file (>24 hours). Starting fresh scan." + Create archive directory: {output_folder}/.archive/ + Archive old state file to: {output_folder}/.archive/project-scan-report-{{timestamp}}.json + Set resume_mode = false + Continue to Step 3 + + + + I found an in-progress workflow state from {{last_updated}}. **Current Progress:** @@ -110,7 +137,7 @@ Your choice [1/2/3]: Create archive directory: {output_folder}/.archive/ Move old state file to: {output_folder}/.archive/project-scan-report-{{timestamp}}.json Set resume_mode = false - Continue to Step 0.5 + Continue to Step 3 @@ -118,12 +145,13 @@ Your choice [1/2/3]: Exit workflow - - Display: "Found old state file (>24 hours). Starting fresh scan." - Archive old state file to: {output_folder}/.archive/project-scan-report-{{timestamp}}.json - Set resume_mode = false - Continue to Step 0.5 - + + + + + Set resume_mode = false + Continue to Step 3 + @@ -149,6 +177,11 @@ Your choice [1/2/3]: Set workflow_mode = "full_rescan" Display: "Starting full project rescan..." Read fully and follow: {installed_path}/workflows/full-scan-instructions.md + Set subworkflow_success = true only if delegated workflow completed without HALT/error + + Sub-workflow failed or was aborted during full rescan. Exiting without marking completion. + Exit workflow + After sub-workflow completes, continue to Step 4 @@ -157,6 +190,11 @@ Your choice [1/2/3]: Set scan_level = "exhaustive" Display: "Starting deep-dive documentation mode..." Read fully and follow: {installed_path}/workflows/deep-dive-instructions.md + Set subworkflow_success = true only if delegated workflow completed without HALT/error + + Sub-workflow failed or was aborted during deep-dive mode. Exiting without marking completion. + Exit workflow + After sub-workflow completes, continue to Step 4 @@ -170,6 +208,11 @@ Your choice [1/2/3]: Set workflow_mode = "initial_scan" Display: "No existing documentation found. Starting initial project scan..." Read fully and follow: {installed_path}/workflows/full-scan-instructions.md + Set subworkflow_success = true only if delegated workflow completed without HALT/error + + Sub-workflow failed or was aborted during initial scan. Exiting without marking completion. + Exit workflow + After sub-workflow completes, continue to Step 4 @@ -178,15 +221,19 @@ Your choice [1/2/3]: - - mode: update - action: complete_workflow - workflow_name: document-project - + Attempt status update in {{status_file_path}}: + - Mark workflow `document-project` as completed + - Persist updated timestamp and completion metadata + - Set status_update_success = true on success + - If write fails, set status_update_success = false and capture status_update_error + - + Status updated! + + ⚠️ Status update skipped: {{status_update_error}} + **✅ Document Project Workflow Complete, {user_name}!** @@ -196,25 +243,21 @@ Your choice [1/2/3]: - Mode: {{workflow_mode}} - Scan Level: {{scan_level}} - Output: {output_folder}/index.md and related files + -{{#if status_file_found}} -**Status Updated:** - -- Progress tracking updated + + **Status Updated:** Progress tracking updated. **Next Steps:** - - **Next required:** {{next_workflow}} ({{next_agent}} agent) +- Run `bmad-help` if you need recommended next workflows. + -Check status anytime with: `workflow-status` -{{else}} -**Next Steps:** -Since no workflow is in progress: - + + **Next Steps:** - Refer to the BMM workflow guide if unsure what to do next -- Or run `workflow-init` to create a workflow path and get guided next steps - {{/if}} - +- Run `bmad-help` to get guided workflow recommendations + diff --git a/test/test-installation-components.js b/test/test-installation-components.js index e89e35887..2c9cc59a1 100644 --- a/test/test-installation-components.js +++ b/test/test-installation-components.js @@ -199,16 +199,19 @@ async function runTests() { try { const builder = new YamlXmlBuilder(); - // Test path resolution logic (if exposed) - // This would test {project-root}, {installed_path}, {config_source} resolution + // Basic path-variable substitution contract used across workflow templates + const testPath = '{project-root}/_bmad/bmm/config.yaml'; + const projectRootStub = path.join(os.tmpdir(), 'bmad-test-project'); + const resolvedPath = testPath.replace('{project-root}', projectRootStub); - const testPath = '{project-root}/bmad/bmm/config.yaml'; - const expectedPattern = /\/bmad\/bmm\/config\.yaml$/; + assert(builder && typeof builder.deepMerge === 'function', 'Path suite uses initialized YamlXmlBuilder instance'); + + assert(resolvedPath.startsWith(projectRootStub), 'Path variable replaces {project-root} with resolved root'); assert( - true, // Placeholder - would test actual resolution - 'Path variable resolution pattern matches expected format', - 'Note: This test validates path resolution logic exists', + resolvedPath.endsWith(path.join(BMAD_FOLDER_NAME, 'bmm', 'config.yaml')), + 'Path variable resolution preserves canonical BMAD folder path', + `Resolved path was: ${resolvedPath}`, ); } catch (error) { assert(false, 'Path resolution works', error.message);