From d05b9f87d50a2203e2774cf21f149d7d6e7afc29 Mon Sep 17 00:00:00 2001 From: sno Date: Thu, 19 Feb 2026 13:47:02 +0100 Subject: [PATCH] refactor: centralize monorepo context detection logic by removing duplicated checks from numerous workflow files and updating related tests. --- .../create-product-brief/workflow.md | 5 - .../research/workflow-domain-research.md | 4 - .../research/workflow-market-research.md | 4 - .../research/workflow-technical-research.md | 4 - .../create-prd/workflow-create-prd.md | 9 - .../create-prd/workflow-edit-prd.md | 4 - .../create-prd/workflow-validate-prd.md | 4 - .../create-ux-design/workflow.md | 4 - .../workflow.md | 5 - .../create-architecture/workflow.md | 5 - .../create-epics-and-stories/workflow.md | 4 - .../correct-course/instructions.md | 10 +- .../create-story/instructions.xml | 17 +- .../dev-story/instructions.xml | 20 +- .../retrospective/instructions.md | 11 - .../sprint-planning/instructions.md | 8 - .../sprint-status/instructions.md | 8 - .../bmad-quick-flow/quick-dev/workflow.md | 5 - .../bmad-quick-flow/quick-spec/workflow.md | 5 - .../document-project/instructions.md | 4 - .../workflows/full-scan-instructions.md | 8 - .../generate-project-context/workflow.md | 6 - src/bmm/workflows/qa/automate/instructions.md | 8 - src/core/workflows/brainstorming/workflow.md | 6 - src/core/workflows/party-mode/workflow.md | 5 - test/test-context-logic-integration.js | 206 ++++++++++++++++++ test/test-monorepo-validation.js | 66 +++--- 27 files changed, 244 insertions(+), 201 deletions(-) create mode 100644 test/test-context-logic-integration.js diff --git a/src/bmm/workflows/1-analysis/create-product-brief/workflow.md b/src/bmm/workflows/1-analysis/create-product-brief/workflow.md index c5764d4ff..14a9084a5 100644 --- a/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +++ b/src/bmm/workflows/1-analysis/create-product-brief/workflow.md @@ -51,11 +51,6 @@ This uses **step-file architecture** for disciplined execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - - `project_name`, `output_folder`, `planning_artifacts`, `user_name`, `communication_language`, `document_output_language`, `user_skill_level` ### 2. First Step EXECUTION diff --git a/src/bmm/workflows/1-analysis/research/workflow-domain-research.md b/src/bmm/workflows/1-analysis/research/workflow-domain-research.md index 58cf01f4e..f17a7d907 100644 --- a/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +++ b/src/bmm/workflows/1-analysis/research/workflow-domain-research.md @@ -20,10 +20,6 @@ main_config: '{project-root}/_bmad/bmm/config.yaml' Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - `project_name`, `output_folder`, `planning_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/bmm/workflows/1-analysis/research/workflow-market-research.md b/src/bmm/workflows/1-analysis/research/workflow-market-research.md index 937a98c4d..06501fdb1 100644 --- a/src/bmm/workflows/1-analysis/research/workflow-market-research.md +++ b/src/bmm/workflows/1-analysis/research/workflow-market-research.md @@ -20,10 +20,6 @@ main_config: '{project-root}/_bmad/bmm/config.yaml' Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - `project_name`, `output_folder`, `planning_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/bmm/workflows/1-analysis/research/workflow-technical-research.md b/src/bmm/workflows/1-analysis/research/workflow-technical-research.md index 6e03f7af1..778def8c7 100644 --- a/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +++ b/src/bmm/workflows/1-analysis/research/workflow-technical-research.md @@ -20,10 +20,6 @@ main_config: '{project-root}/_bmad/bmm/config.yaml' Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - `project_name`, `output_folder`, `planning_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md index 8b50a309d..7007ab8fb 100644 --- a/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +++ b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md @@ -46,15 +46,6 @@ This uses **step-file architecture** for disciplined execution: ## INITIALIZATION SEQUENCE -### 1. Configuration Loading - -Load and read full config from {main_config} and resolve basic variables. - -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - - `project_name`, `output_folder`, `planning_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` - `date` as system-generated current datetime diff --git a/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md index ada593f90..37731b4c5 100644 --- a/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +++ b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md @@ -50,10 +50,6 @@ This uses **step-file architecture** for disciplined execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - `project_name`, `output_folder`, `planning_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` - `date` as system-generated current datetime diff --git a/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md index 8e09bb612..7b17c8026 100644 --- a/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +++ b/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md @@ -50,10 +50,6 @@ This uses **step-file architecture** for disciplined execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - `project_name`, `output_folder`, `planning_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md b/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md index 5b5be72c4..f00c76256 100644 --- a/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +++ b/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md @@ -26,10 +26,6 @@ This uses **micro-file architecture** for disciplined execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - `project_name`, `output_folder`, `planning_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md b/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md index dad036735..58b12630d 100644 --- a/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +++ b/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md @@ -46,11 +46,6 @@ description: 'Critical validation workflow that assesses PRD, Architecture, and Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - - `project_name`, `output_folder`, `planning_artifacts`, `user_name`, `communication_language`, `document_output_language` - ✅ YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}` diff --git a/src/bmm/workflows/3-solutioning/create-architecture/workflow.md b/src/bmm/workflows/3-solutioning/create-architecture/workflow.md index ceff8cb7e..100432e4e 100644 --- a/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +++ b/src/bmm/workflows/3-solutioning/create-architecture/workflow.md @@ -29,11 +29,6 @@ This uses **micro-file architecture** for disciplined execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: - `project_name`, `output_folder`, `planning_artifacts`, `user_name` diff --git a/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md b/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md index 3d42212ec..6d449749e 100644 --- a/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +++ b/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md @@ -50,10 +50,6 @@ This uses **step-file architecture** for disciplined execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - `project_name`, `output_folder`, `planning_artifacts`, `user_name`, `communication_language`, `document_output_language` - ✅ YOU MUST ALWAYS SPEAK OUTPUT In your Agent communication style with the config `{communication_language}` diff --git a/src/bmm/workflows/4-implementation/correct-course/instructions.md b/src/bmm/workflows/4-implementation/correct-course/instructions.md index 58d00c658..023c8c4fd 100644 --- a/src/bmm/workflows/4-implementation/correct-course/instructions.md +++ b/src/bmm/workflows/4-implementation/correct-course/instructions.md @@ -40,15 +40,7 @@ - [x] Done - Item completed successfully - [N/A] Skip - Item not applicable to this change - [!] Action-needed - Item requires attention or follow-up -### 1. Configuration Loading - -Load and read full config from {main_config} and resolve basic variables. - -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - Maintain running notes of findings and impacts discovered + Present checklist progress after each major section Identify blocking issues and work with user to resolve before continuing diff --git a/src/bmm/workflows/4-implementation/create-story/instructions.xml b/src/bmm/workflows/4-implementation/create-story/instructions.xml index 279585a7a..d7b7158aa 100644 --- a/src/bmm/workflows/4-implementation/create-story/instructions.xml +++ b/src/bmm/workflows/4-implementation/create-story/instructions.xml @@ -18,22 +18,7 @@ 🎯 ZERO USER INTERVENTION: Process should be fully automated except for initial epic/story selection or missing documents - - Read content as project_suffix - - Trim whitespace and newlines from project_suffix - - 🚫 Security Error: Invalid project context path detected. - HALT - - - 🚫 Error: Project context must only contain alphanumeric characters, dots, dashes, or underscores. - HALT - - Override output_folder to {project-root}/_bmad-output/{project_suffix} - Output "Monorepo context detected. Output folder redirected to: {output_folder}" - - + Parse user-provided story path: extract epic_num, story_num, story_title from format like "1-2-user-auth" Set {{epic_num}}, {{story_num}}, {{story_key}} from user input diff --git a/src/bmm/workflows/4-implementation/dev-story/instructions.xml b/src/bmm/workflows/4-implementation/dev-story/instructions.xml index 4767e9624..aa8308404 100644 --- a/src/bmm/workflows/4-implementation/dev-story/instructions.xml +++ b/src/bmm/workflows/4-implementation/dev-story/instructions.xml @@ -13,25 +13,7 @@ User skill level ({user_skill_level}) affects conversation style ONLY, not code updates. - - Read content as project_suffix - - Trim whitespace and newlines from project_suffix - - 🚫 Security Error: Invalid project context path detected. - HALT - - - 🚫 Error: Project context must only contain alphanumeric characters, dots, dashes, or underscores. - HALT - - - 🚫 Error: Project context name is too long (max 100 characters). - HALT - - Override output_folder to {project-root}/_bmad-output/{project_suffix} - - + Use {{story_path}} directly Read COMPLETE story file diff --git a/src/bmm/workflows/4-implementation/retrospective/instructions.md b/src/bmm/workflows/4-implementation/retrospective/instructions.md index fd1f7d52e..762f5a6ef 100644 --- a/src/bmm/workflows/4-implementation/retrospective/instructions.md +++ b/src/bmm/workflows/4-implementation/retrospective/instructions.md @@ -3,17 +3,6 @@ The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml You MUST have already loaded and processed: {installed_path}/workflow.yaml -## 1. Configuration Loading - -Load and read full config from {main_config} and resolve basic variables. - -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` -3. Re-derive dependent path variables to reflect the new `output_folder`: - - `implementation_artifacts`: `{output_folder}/implementation` - - `planning_artifacts`: `{output_folder}/planning` Communicate all responses in {communication_language} and language MUST be tailored to {user_skill_level} Generate all documents in {document_output_language} diff --git a/src/bmm/workflows/4-implementation/sprint-planning/instructions.md b/src/bmm/workflows/4-implementation/sprint-planning/instructions.md index 2fbd0c1da..e87110c27 100644 --- a/src/bmm/workflows/4-implementation/sprint-planning/instructions.md +++ b/src/bmm/workflows/4-implementation/sprint-planning/instructions.md @@ -4,14 +4,6 @@ You MUST have already loaded and processed: {project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml ## 📚 Document Discovery -### 1. Configuration Loading - -Load and read full config from {main_config} and resolve basic variables. - -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - Full Epic Loading diff --git a/src/bmm/workflows/4-implementation/sprint-status/instructions.md b/src/bmm/workflows/4-implementation/sprint-status/instructions.md index fc589a22a..43b5e1316 100644 --- a/src/bmm/workflows/4-implementation/sprint-status/instructions.md +++ b/src/bmm/workflows/4-implementation/sprint-status/instructions.md @@ -2,14 +2,6 @@ The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml -## 1. Configuration Loading - -Load and read full config from {main_config} and resolve basic variables. - -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` You MUST have already loaded and processed: {project-root}/_bmad/bmm/workflows/4-implementation/sprint-status/workflow.yaml Modes: interactive (default), validate, data ⚠️ ABSOLUTELY NO TIME ESTIMATES. Do NOT mention hours, days, weeks, or timelines. diff --git a/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md b/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md index f156f4b4e..56fa658e1 100644 --- a/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +++ b/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md @@ -27,11 +27,6 @@ This uses **step-file architecture** for focused execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: - `user_name`, `communication_language`, `user_skill_level` diff --git a/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md b/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md index 08afd36b1..ad027deb7 100644 --- a/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +++ b/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md @@ -68,11 +68,6 @@ This uses **step-file architecture** for disciplined execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: - `project_name`, `planning_artifacts`, `implementation_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/bmm/workflows/document-project/instructions.md b/src/bmm/workflows/document-project/instructions.md index 35fcc290d..d6a685c3d 100644 --- a/src/bmm/workflows/document-project/instructions.md +++ b/src/bmm/workflows/document-project/instructions.md @@ -83,10 +83,6 @@ Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` Check if {project_knowledge}/index.md exists diff --git a/src/bmm/workflows/document-project/workflows/full-scan-instructions.md b/src/bmm/workflows/document-project/workflows/full-scan-instructions.md index e43435bd2..6e3a3fed3 100644 --- a/src/bmm/workflows/document-project/workflows/full-scan-instructions.md +++ b/src/bmm/workflows/document-project/workflows/full-scan-instructions.md @@ -95,15 +95,7 @@ Your choice [1/2/3]: - Best for: Quick project overview, initial understanding - File reading: Minimal (configs, README, package.json, etc.) -## 1. Configuration Loading -Load and read full config from {main_config} and resolve basic variables. - -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` -json, etc.) **2. Deep Scan** (10-30 minutes) diff --git a/src/bmm/workflows/generate-project-context/workflow.md b/src/bmm/workflows/generate-project-context/workflow.md index 88b8e8270..749016fec 100644 --- a/src/bmm/workflows/generate-project-context/workflow.md +++ b/src/bmm/workflows/generate-project-context/workflow.md @@ -29,12 +29,6 @@ This uses **micro-file architecture** for disciplined execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: - `project_name`, `output_folder`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/bmm/workflows/qa/automate/instructions.md b/src/bmm/workflows/qa/automate/instructions.md index 91ee2f771..561a80581 100644 --- a/src/bmm/workflows/qa/automate/instructions.md +++ b/src/bmm/workflows/qa/automate/instructions.md @@ -18,14 +18,6 @@ Check project for existing test framework: - Search online for current recommended test framework for that stack - Suggest the meta framework and use it (or ask user to confirm) -### 1. Configuration Loading - -Load and read full config from {main_config} and resolve basic variables. - -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` ### Step 1: Identify Features diff --git a/src/core/workflows/brainstorming/workflow.md b/src/core/workflows/brainstorming/workflow.md index f93ceb6d2..fb983a730 100644 --- a/src/core/workflows/brainstorming/workflow.md +++ b/src/core/workflows/brainstorming/workflow.md @@ -36,12 +36,6 @@ This uses **micro-file architecture** for disciplined execution: Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - -Load config from `{project-root}/_bmad/core/config.yaml` and resolve: - `project_name`, `output_folder`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/core/workflows/party-mode/workflow.md b/src/core/workflows/party-mode/workflow.md index da61fb165..183968f7d 100644 --- a/src/core/workflows/party-mode/workflow.md +++ b/src/core/workflows/party-mode/workflow.md @@ -29,11 +29,6 @@ This uses **micro-file architecture** with **sequential conversation orchestrati Load and read full config from {main_config} and resolve basic variables. -**Monorepo Context Check:** -1. Check if `{project-root}/_bmad/.current_project` exists. -2. If it exists, read its content as `{project_suffix}` and override output folder: - - `output_folder`: `{project-root}/_bmad-output/{project_suffix}` - - `project_name`, `output_folder`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` - `date` as a system-generated value diff --git a/test/test-context-logic-integration.js b/test/test-context-logic-integration.js new file mode 100644 index 000000000..75a40d8d4 --- /dev/null +++ b/test/test-context-logic-integration.js @@ -0,0 +1,206 @@ +/** + * Context Logic Integration Tests + * + * Validates the centralized monorepo context logic deduplication: + * 1. context-logic.js exports a valid XML block + * 2. All workflow templates that need it use the {{monorepo_context_logic}} placeholder + * 3. No stale hardcoded blocks exist in templates + * 4. src/core/tasks/workflow.xml uses the placeholder (not a hardcoded block) + * 5. All JS consumers correctly import context-logic.js + * 6. MONOREPO_CONTEXT_LOGIC string integrity (key fields are present) + */ + +const fs = require('fs-extra'); +const path = require('node:path'); + +// ANSI colors +const c = { + reset: '\u001B[0m', + green: '\u001B[32m', + red: '\u001B[31m', + yellow: '\u001B[33m', + cyan: '\u001B[36m', + dim: '\u001B[2m', +}; + +let passed = 0; +let failed = 0; + +function ok(condition, testName, detail = '') { + if (condition) { + console.log(`${c.green}✓${c.reset} ${testName}`); + passed++; + } else { + console.log(`${c.red}✗${c.reset} ${testName}`); + if (detail) console.log(` ${c.dim}${detail}${c.reset}`); + failed++; + } +} + +async function readFile(p) { + return fs.readFile(p, 'utf8'); +} + +async function exists(p) { + return fs.pathExists(p); +} + +async function runTests() { + console.log(`${c.cyan}============================================================`); + console.log(' Context Logic Integration Tests'); + console.log(`============================================================${c.reset}\n`); + + const root = path.join(__dirname, '..'); + const sharedDir = path.join(root, 'tools/cli/installers/lib/ide/shared'); + const templatesDir = path.join(root, 'tools/cli/installers/lib/ide/templates'); + const combinedDir = path.join(templatesDir, 'combined'); + + // ──────────────────────────────────────────────────────────── + // Suite 1: context-logic.js module integrity + // ──────────────────────────────────────────────────────────── + console.log(`${c.yellow}Suite 1: context-logic.js module integrity${c.reset}\n`); + + const contextLogicPath = path.join(sharedDir, 'context-logic.js'); + ok(await exists(contextLogicPath), 'context-logic.js file exists'); + + let MONOREPO_CONTEXT_LOGIC; + try { + const mod = require(contextLogicPath); + ok(typeof mod.MONOREPO_CONTEXT_LOGIC === 'string', 'exports MONOREPO_CONTEXT_LOGIC as a string'); + ok(mod.MONOREPO_CONTEXT_LOGIC.length > 0, 'MONOREPO_CONTEXT_LOGIC is non-empty'); + MONOREPO_CONTEXT_LOGIC = mod.MONOREPO_CONTEXT_LOGIC; + } catch (error) { + ok(false, 'context-logic.js is require()-able', error.message); + MONOREPO_CONTEXT_LOGIC = ''; + } + + // Key content checks + ok(MONOREPO_CONTEXT_LOGIC.includes(' tag'); + ok(MONOREPO_CONTEXT_LOGIC.includes(''), 'has closing tag'); + ok(MONOREPO_CONTEXT_LOGIC.includes('#project:NAME'), 'documents #project:NAME syntax'); + ok(MONOREPO_CONTEXT_LOGIC.includes('#p:NAME'), 'documents #p:NAME short alias'); + ok(MONOREPO_CONTEXT_LOGIC.includes('.current_project'), 'includes .current_project fallback logic'); + ok(MONOREPO_CONTEXT_LOGIC.includes('path traversal'), 'includes path traversal security check'); + ok(MONOREPO_CONTEXT_LOGIC.includes('output_folder'), 'overrides output_folder path variable'); + ok(MONOREPO_CONTEXT_LOGIC.includes('planning_artifacts'), 'overrides planning_artifacts path variable'); + ok(MONOREPO_CONTEXT_LOGIC.includes('HALT'), 'halts on security violation'); + console.log(''); + + // ──────────────────────────────────────────────────────────── + // Suite 2: JS consumers import context-logic.js correctly + // ──────────────────────────────────────────────────────────── + console.log(`${c.yellow}Suite 2: JS consumers import context-logic.js${c.reset}\n`); + + const consumers = [ + { + file: 'tools/cli/installers/lib/core/installer.js', + expectedImport: "require('../ide/shared/context-logic')", + }, + { + file: 'tools/cli/installers/lib/ide/_config-driven.js', + expectedImport: "require('./shared/context-logic')", + }, + { + file: 'tools/cli/installers/lib/ide/shared/workflow-command-generator.js', + expectedImport: "require('./context-logic')", + }, + ]; + + for (const { file, expectedImport } of consumers) { + const fullPath = path.join(root, file); + const content = await readFile(fullPath); + ok(content.includes(expectedImport), `${path.basename(file)} imports context-logic correctly`); + ok(content.includes("replaceAll('{{monorepo_context_logic}}'"), `${path.basename(file)} uses replaceAll for placeholder`); + } + console.log(''); + + // ──────────────────────────────────────────────────────────── + // Suite 3: Templates use placeholder, not hardcoded blocks + // ──────────────────────────────────────────────────────────── + console.log(`${c.yellow}Suite 3: Templates use {{monorepo_context_logic}} placeholder${c.reset}\n`); + + // These templates MUST have the placeholder (they are rendered directly as IDE workflow commands) + const mustHavePlaceholder = [ + path.join(templatesDir, 'workflow-command-template.md'), + path.join(templatesDir, 'workflow-commander.md'), + path.join(combinedDir, 'antigravity.md'), + path.join(combinedDir, 'claude-workflow.md'), + path.join(combinedDir, 'claude-workflow-yaml.md'), + path.join(combinedDir, 'default-workflow.md'), + path.join(combinedDir, 'default-workflow-yaml.md'), + path.join(combinedDir, 'kiro-workflow.md'), + path.join(combinedDir, 'opencode-workflow.md'), + path.join(combinedDir, 'windsurf-workflow.md'), + ]; + + for (const filePath of mustHavePlaceholder) { + const rel = path.relative(root, filePath); + const content = await readFile(filePath); + ok(content.includes('{{monorepo_context_logic}}'), `${path.basename(filePath)} has {{monorepo_context_logic}} placeholder`); + // Must NOT have raw hardcoded block (only the shared module should have it) + ok(!content.includes(' block`); + } + console.log(''); + + // ──────────────────────────────────────────────────────────── + // Suite 4: No rogue hardcoded blocks anywhere in templates dir + // ──────────────────────────────────────────────────────────── + console.log(`${c.yellow}Suite 4: No hardcoded blocks in templates directory${c.reset}\n`); + + const walkDir = async (dir) => { + const entries = await fs.readdir(dir, { withFileTypes: true }); + const files = []; + for (const e of entries) { + const full = path.join(dir, e.name); + if (e.isDirectory()) files.push(...(await walkDir(full))); + else if (e.isFile()) files.push(full); + } + return files; + }; + + const allTemplateFiles = await walkDir(templatesDir); + const rogueFiles = []; + for (const f of allTemplateFiles) { + const content = await readFile(f); + if (content.includes(' blocks in templates (found ${rogueFiles.length})`, + rogueFiles.length > 0 ? `Rogue files: ${rogueFiles.join(', ')}` : '', + ); + console.log(''); + + // ──────────────────────────────────────────────────────────── + // Suite 5: src/core/tasks/workflow.xml uses placeholder + // ──────────────────────────────────────────────────────────── + console.log(`${c.yellow}Suite 5: src/core/tasks/workflow.xml uses placeholder${c.reset}\n`); + + const srcWorkflowXml = path.join(root, 'src/core/tasks/workflow.xml'); + ok(await exists(srcWorkflowXml), 'src/core/tasks/workflow.xml exists'); + const srcXmlContent = await readFile(srcWorkflowXml); + ok(srcXmlContent.includes('{{monorepo_context_logic}}'), 'workflow.xml (src) uses {{monorepo_context_logic}} placeholder'); + ok(!srcXmlContent.includes(' block'); + + // ──────────────────────────────────────────────────────────── + // Results + // ──────────────────────────────────────────────────────────── + console.log(`\n${c.cyan}============================================================`); + console.log(` Results: ${c.green}${passed} passed${c.reset}${c.cyan}, ${c.red}${failed} failed${c.reset}${c.cyan}`); + console.log(`============================================================${c.reset}\n`); + + if (failed === 0) { + console.log(`${c.green}✨ All context-logic integration tests passed!${c.reset}\n`); + process.exit(0); + } else { + console.log(`${c.red}❌ ${failed} test(s) failed${c.reset}\n`); + process.exit(1); + } +} + +runTests().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/test/test-monorepo-validation.js b/test/test-monorepo-validation.js index bab840708..0b696390e 100644 --- a/test/test-monorepo-validation.js +++ b/test/test-monorepo-validation.js @@ -1,9 +1,19 @@ /** * Monorepo Support Validation Tests * - * Verifies that: + * Architecture after deduplication: + * - Monorepo context logic lives ONLY in context-logic.js + * - workflow.xml (src) uses {{monorepo_context_logic}} placeholder → injected at install time + * - Individual source workflow files do NOT have inline checks (that's the deduplication!) + * - Only code-review/instructions.xml, dev-story/instructions.xml, create-story/instructions.xml + * and advanced-elicitation/workflow.xml are XML workflows checked; XML workflows that go through + * workflow.xml no longer need inline checks. + * + * Verifies: * 1. The set-project workflow is correctly registered. - * 2. All core and BMM workflows contain the monorepo context logic. + * 2. No source workflow file has a stale inline "Monorepo Context Check" block. + * 3. Only the canonical SINGLE source (context-logic.js) defines the check. + * 4. set-project implementation still manages .current_project. */ const fs = require('fs-extra'); @@ -55,46 +65,39 @@ async function runTests() { console.log(''); - // 2. Verify context logic in workflows - console.log(`${colors.yellow}Test Suite 2: Workflow Context Logic${colors.reset}\n`); + // 2. Verify NO stale inline "Monorepo Context Check" blocks in source workflow files + // These are redundant since workflow.xml now handles context injection via context-logic.js + console.log(`${colors.yellow}Test Suite 2: No Stale Inline Monorepo Context Checks${colors.reset}\n`); + console.log(` ${colors.dim}(Inline checks were moved to workflow.xml via context-logic.js)${colors.reset}\n`); const workflowFiles = glob.sync('src/{core,bmm}/workflows/**/*.{md,xml}', { cwd: projectRoot }); - // Workflows that MUST have the check - const requiredWorkflows = [ - 'brainstorming', - 'party-mode', - 'create-product-brief', - 'create-prd', - 'create-architecture', - 'code-review', - 'create-story', - 'dev-story', - 'set-project', // Should not have the check itself, but let's exclude it - ]; - for (const file of workflowFiles) { - const basename = path.basename(path.dirname(file)); - if (basename === 'set-project' || basename === '0-context') continue; + // skip the context-logic source itself (it's the canonical source) + if (file.includes('context-logic')) continue; const content = await fs.readFile(path.join(projectRoot, file), 'utf8'); - const isXml = file.endsWith('.xml'); - if (isXml) { - assert(content.includes('_bmad/.current_project'), `XML workflow contains context check: ${file}`); - } else { - // Only check Markdown files that look like main workflow/instruction files - const filename = path.basename(file); - if (filename.includes('workflow') || filename.includes('instructions')) { - assert(content.includes('_bmad/.current_project'), `Markdown workflow contains context check: ${file}`); - } - } + assert(!content.includes('**Monorepo Context Check:**'), `No stale inline check block in: ${file}`); } console.log(''); - // 3. Verify set-project implementation - console.log(`${colors.yellow}Test Suite 3: set-project Implementation${colors.reset}\n`); + // 3. Verify canonical source is context-logic.js (single source of truth) + console.log(`${colors.yellow}Test Suite 3: Single Source of Truth${colors.reset}\n`); + + const contextLogicPath = path.join(projectRoot, 'tools/cli/installers/lib/ide/shared/context-logic.js'); + assert(await fs.pathExists(contextLogicPath), 'context-logic.js exists as canonical source'); + + const srcWorkflowXml = path.join(projectRoot, 'src/core/tasks/workflow.xml'); + const xmlContent = await fs.readFile(srcWorkflowXml, 'utf8'); + assert(xmlContent.includes('{{monorepo_context_logic}}'), 'workflow.xml uses {{monorepo_context_logic}} placeholder'); + assert(!xmlContent.includes('**Monorepo Context Check:**'), 'workflow.xml has no stale inline check'); + + console.log(''); + + // 4. Verify set-project implementation + console.log(`${colors.yellow}Test Suite 4: set-project Implementation${colors.reset}\n`); try { const setProjectPath = path.join(projectRoot, 'src/bmm/workflows/0-context/set-project/workflow.md'); const exists = await fs.pathExists(setProjectPath); @@ -102,6 +105,7 @@ async function runTests() { if (exists) { const content = await fs.readFile(setProjectPath, 'utf8'); assert(content.includes('_bmad/.current_project'), 'set-project implementation manages .current_project'); + assert(content.includes('my-app'), 'set-project examples use generic public-friendly names'); } } catch (error) { assert(false, 'set-project check failed', error.message);