bmad-method-test-architecture-enterprise 1.17.0 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@
12
12
  "name": "bmad-method-test-architecture-enterprise",
13
13
  "source": "./",
14
14
  "description": "Master Test Architect module for quality strategy, test automation, CI/CD quality gates, and structured testing education. Part of the BMad Method ecosystem.",
15
- "version": "1.17.0",
15
+ "version": "1.17.1",
16
16
  "author": {
17
17
  "name": "Murat K Ozcan (TEA Creator) & Brian (BMad) Madison"
18
18
  },
package/CHANGELOG.md CHANGED
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14
14
  - `actions/setup-node@v6`
15
15
  - `actions/create-github-app-token@v3`
16
16
  - Publish releases now use `[Unreleased]` changelog notes before falling back to generated GitHub release notes when an exact version section is missing.
17
+ - Documented workflow-local knowledge resources as intentional self-contained skill packaging and added validation for workflow-local knowledge indexes.
17
18
 
18
19
  ---
19
20
 
package/README.md CHANGED
@@ -43,9 +43,12 @@ TEA has two layers of files, and each has a specific job:
43
43
  | `steps-v/*.md` | **Validate** steps — always 1 file: evaluate against checklist | On demand |
44
44
  | `checklist.md` | Validation criteria — what "done" looks like for this workflow | Read by steps-v |
45
45
  | `*-template.md` | Output skeleton with `{PLACEHOLDER}` vars — steps fill these in to produce the final artifact | Read by steps-c when generating output |
46
- | `src/agents/bmad-tea/resources/tea-index.csv` | Knowledge fragment index — id, name, tags, tier (core/extended/specialized), file path | Read to decide which shared TEA fragments to load |
46
+ | `src/agents/bmad-tea/resources/tea-index.csv` | Agent-level knowledge fragment index — id, name, tags, tier (core/extended/specialized), file path | Read by the TEA agent for direct recommendations |
47
+ | `src/workflows/testarch/<workflow>/resources/` | Workflow-local knowledge index and fragments | Read by workflow steps from that workflow's skill root |
47
48
  | `resources/knowledge/*.md` | Reusable fragments — standards, patterns, API references | Selectively read into context based on tier + config |
48
49
 
50
+ Workflow resource directories intentionally duplicate the TEA knowledge base. Each workflow skill must stay self-contained so it can be installed, copied, or invoked without reaching across skill boundaries. When knowledge changes, propagate the intended updates to the affected workflow resource directories instead of replacing them with a central runtime path.
51
+
49
52
  ```mermaid
50
53
  flowchart LR
51
54
  U[User] --> A[Agent Persona]
@@ -96,6 +96,8 @@ network-first,Network-First Safeguards,Intercept-before-navigate workflow,"netwo
96
96
  fixture-architecture,Fixture Architecture,Composable fixture patterns,"fixtures,architecture",knowledge/fixture-architecture.md
97
97
  ```
98
98
 
99
+ The agent-level `resources/` directory is the reference catalog for Murat. Workflow skills also carry their own `resources/tea-index.csv` and `resources/knowledge/` directories. That duplication is intentional: workflow step frontmatter resolves `knowledgeIndex: './resources/tea-index.csv'` from `{skill-root}`, keeping each workflow skill modular and self-contained.
100
+
99
101
  **2. Workflow Loads Relevant Fragments**
100
102
 
101
103
  When user runs `atdd`:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method-test-architecture-enterprise",
4
- "version": "1.17.0",
4
+ "version": "1.17.1",
5
5
  "description": "Master Test Architect for quality strategy, test automation, and release gates",
6
6
  "keywords": [
7
7
  "bmad",
@@ -12,6 +12,7 @@
12
12
 
13
13
  const path = require('node:path');
14
14
  const fs = require('node:fs/promises');
15
+ const { parse } = require('csv-parse/sync');
15
16
  const yaml = require('js-yaml');
16
17
 
17
18
  async function pathExists(filePath) {
@@ -23,6 +24,11 @@ async function pathExists(filePath) {
23
24
  }
24
25
  }
25
26
 
27
+ function extractFrontmatter(content) {
28
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
29
+ return match ? match[1] : '';
30
+ }
31
+
26
32
  // ANSI colors
27
33
  const colors = {
28
34
  reset: '\u001B[0m',
@@ -253,10 +259,12 @@ async function runTests() {
253
259
  ];
254
260
 
255
261
  for (const dirName of workflowDirs) {
262
+ const workflowDir = path.join(projectRoot, `src/workflows/testarch/${dirName}`);
256
263
  const skillMdPath = path.join(projectRoot, `src/workflows/testarch/${dirName}/SKILL.md`);
257
264
  const customizeTomlPath = path.join(projectRoot, `src/workflows/testarch/${dirName}/customize.toml`);
258
265
  const workflowYamlPath = path.join(projectRoot, `src/workflows/testarch/${dirName}/workflow.yaml`);
259
266
  const instructionsMdPath = path.join(projectRoot, `src/workflows/testarch/${dirName}/instructions.md`);
267
+ let workflowKnowledgeIndexValidated = false;
260
268
 
261
269
  if (await pathExists(skillMdPath)) {
262
270
  try {
@@ -344,6 +352,7 @@ async function runTests() {
344
352
  const stepPath = path.join(stepDirPath, fileName);
345
353
  try {
346
354
  const stepContent = await fs.readFile(stepPath, 'utf8');
355
+ const frontmatter = extractFrontmatter(stepContent);
347
356
  const stepLabel = `${dirName}/${stepDir}/${fileName}`;
348
357
 
349
358
  assert(!stepContent.includes("nextStepFile: './"), `${stepLabel} has no cwd-sensitive nextStepFile`);
@@ -371,6 +380,46 @@ async function runTests() {
371
380
  if (stepContent.includes('workflowPath:')) {
372
381
  assert(stepContent.includes("workflowPath: '{skill-root}'"), `${stepLabel} anchors workflowPath to {skill-root}`);
373
382
  }
383
+
384
+ if (frontmatter.includes('knowledgeIndex:')) {
385
+ const knowledgeIndexMatch = frontmatter.match(/^knowledgeIndex:\s*['"]([^'"]+)['"]/m);
386
+ assert(Boolean(knowledgeIndexMatch), `${stepLabel} declares a parseable knowledgeIndex`);
387
+
388
+ const knowledgeIndexReference = knowledgeIndexMatch ? knowledgeIndexMatch[1] : '';
389
+ const knowledgeIndexPath = path.resolve(workflowDir, knowledgeIndexReference);
390
+ const expectedKnowledgeIndexPath = path.join(workflowDir, 'resources', 'tea-index.csv');
391
+
392
+ assert(knowledgeIndexPath === expectedKnowledgeIndexPath, `${stepLabel} uses the workflow-local knowledge index`);
393
+ assert(await pathExists(knowledgeIndexPath), `${stepLabel} knowledgeIndex target exists`);
394
+
395
+ if (!workflowKnowledgeIndexValidated && (await pathExists(knowledgeIndexPath))) {
396
+ const records = parse(await fs.readFile(knowledgeIndexPath, 'utf8'), { columns: true, skip_empty_lines: true });
397
+ const workflowKnowledgeDir = path.join(path.dirname(knowledgeIndexPath), 'knowledge');
398
+ const workflowKnowledgeFiles = (await fs.readdir(workflowKnowledgeDir)).filter((name) => name.endsWith('.md'));
399
+ const missingFragments = [];
400
+
401
+ for (const record of records) {
402
+ if (!record.fragment_file) {
403
+ missingFragments.push(`${record.id || '<missing-id>'}: missing fragment_file`);
404
+ continue;
405
+ }
406
+
407
+ const fragmentPath = path.resolve(path.dirname(knowledgeIndexPath), record.fragment_file);
408
+ if (!(await pathExists(fragmentPath))) {
409
+ missingFragments.push(record.fragment_file);
410
+ }
411
+ }
412
+
413
+ assert(
414
+ records.length === workflowKnowledgeFiles.length,
415
+ `${dirName}/resources/tea-index.csv line count matches workflow-local fragments`,
416
+ `Found ${records.length} records for ${workflowKnowledgeFiles.length} fragments`,
417
+ );
418
+ assert(missingFragments.length === 0, `${dirName}/resources/tea-index.csv fragment files exist`, missingFragments.join(', '));
419
+
420
+ workflowKnowledgeIndexValidated = true;
421
+ }
422
+ }
374
423
  } catch (error) {
375
424
  assert(false, `${dirName}/${stepDir}/${fileName} validates`, error.message);
376
425
  }