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
-
-
- HALT
-
-
-
- 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
-
-
- HALT
-
-
-
- HALT
-
-
-
- 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);