fraim-framework 2.0.55 โ 2.0.57
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.
- package/CHANGELOG.md +10 -0
- package/dist/src/cli/commands/init-project.js +10 -4
- package/dist/src/cli/setup/mcp-config-generator.js +23 -15
- package/dist/src/local-mcp-server/stdio-server.js +207 -0
- package/dist/src/utils/validate-workflows.js +101 -0
- package/dist/src/utils/workflow-parser.js +81 -0
- package/package.json +16 -11
- package/registry/scripts/pdf-styles.css +172 -0
- package/registry/scripts/prep-issue.sh +46 -4
- package/registry/scripts/profile-server.ts +131 -130
- package/registry/stubs/workflows/customer-development/user-survey-dispatch.md +1 -1
- package/registry/stubs/workflows/customer-development/users-to-target.md +1 -1
- package/registry/stubs/workflows/product-building/design.md +1 -1
- package/registry/stubs/workflows/product-building/implement.md +1 -1
- package/Claude.md +0 -1
- package/dist/registry/ai-manager-rules/design-phases/design-completeness-review.md +0 -73
- package/dist/registry/ai-manager-rules/design-phases/design-design.md +0 -145
- package/dist/registry/ai-manager-rules/implement-phases/implement-code.md +0 -283
- package/dist/registry/ai-manager-rules/implement-phases/implement-completeness-review.md +0 -120
- package/dist/registry/ai-manager-rules/implement-phases/implement-regression.md +0 -173
- package/dist/registry/ai-manager-rules/implement-phases/implement-repro.md +0 -104
- package/dist/registry/ai-manager-rules/implement-phases/implement-scoping.md +0 -100
- package/dist/registry/ai-manager-rules/implement-phases/implement-smoke.md +0 -237
- package/dist/registry/ai-manager-rules/implement-phases/implement-spike.md +0 -121
- package/dist/registry/ai-manager-rules/implement-phases/implement-validate.md +0 -375
- package/dist/registry/ai-manager-rules/retrospective.md +0 -116
- package/dist/registry/ai-manager-rules/shared-phases/address-pr-feedback.md +0 -188
- package/dist/registry/ai-manager-rules/shared-phases/submit-pr.md +0 -202
- package/dist/registry/ai-manager-rules/shared-phases/wait-for-pr-review.md +0 -170
- package/dist/registry/ai-manager-rules/spec-phases/spec-competitor-analysis.md +0 -105
- package/dist/registry/ai-manager-rules/spec-phases/spec-completeness-review.md +0 -66
- package/dist/registry/ai-manager-rules/spec-phases/spec-spec.md +0 -139
- package/dist/registry/providers/ado.json +0 -19
- package/dist/registry/providers/github.json +0 -19
- package/dist/registry/scripts/cleanup-branch.js +0 -287
- package/dist/registry/scripts/evaluate-code-quality.js +0 -66
- package/dist/registry/scripts/exec-with-timeout.js +0 -142
- package/dist/registry/scripts/generate-engagement-emails.js +0 -705
- package/dist/registry/scripts/newsletter-helpers.js +0 -671
- package/dist/registry/scripts/profile-server.js +0 -388
- package/dist/registry/scripts/run-thank-you-workflow.js +0 -92
- package/dist/registry/scripts/send-newsletter-simple.js +0 -85
- package/dist/registry/scripts/send-thank-you-emails.js +0 -54
- package/dist/registry/scripts/validate-openapi-limits.js +0 -311
- package/dist/registry/scripts/validate-test-coverage.js +0 -262
- package/dist/registry/scripts/verify-test-coverage.js +0 -66
- package/dist/scripts/build-stub-registry.js +0 -108
- package/dist/src/ai-manager/ai-manager.js +0 -482
- package/dist/src/ai-manager/phase-flow.js +0 -357
- package/dist/src/ai-manager/types.js +0 -5
- package/dist/src/fraim-mcp-server.js +0 -1885
- package/dist/tests/debug-tools.js +0 -80
- package/dist/tests/shared-server-utils.js +0 -57
- package/dist/tests/test-add-ide.js +0 -283
- package/dist/tests/test-ai-coach-edge-cases.js +0 -420
- package/dist/tests/test-ai-coach-mcp-integration.js +0 -450
- package/dist/tests/test-ai-coach-performance.js +0 -328
- package/dist/tests/test-ai-coach-phase-content.js +0 -264
- package/dist/tests/test-ai-coach-workflows.js +0 -514
- package/dist/tests/test-cli.js +0 -228
- package/dist/tests/test-client-scripts-validation.js +0 -167
- package/dist/tests/test-complete-setup-flow.js +0 -110
- package/dist/tests/test-config-system.js +0 -279
- package/dist/tests/test-debug-session.js +0 -134
- package/dist/tests/test-end-to-end-hybrid-validation.js +0 -328
- package/dist/tests/test-enhanced-session-init.js +0 -188
- package/dist/tests/test-first-run-journey.js +0 -368
- package/dist/tests/test-fraim-issues.js +0 -59
- package/dist/tests/test-genericization.js +0 -44
- package/dist/tests/test-hybrid-script-execution.js +0 -340
- package/dist/tests/test-ide-detector.js +0 -46
- package/dist/tests/test-improved-setup.js +0 -121
- package/dist/tests/test-mcp-config-generator.js +0 -99
- package/dist/tests/test-mcp-connection.js +0 -107
- package/dist/tests/test-mcp-issue-integration.js +0 -156
- package/dist/tests/test-mcp-lifecycle-methods.js +0 -240
- package/dist/tests/test-mcp-shared-server.js +0 -308
- package/dist/tests/test-mcp-template-processing.js +0 -160
- package/dist/tests/test-modular-issue-tracking.js +0 -165
- package/dist/tests/test-node-compatibility.js +0 -95
- package/dist/tests/test-npm-install.js +0 -68
- package/dist/tests/test-package-size.js +0 -108
- package/dist/tests/test-pr-review-workflow.js +0 -307
- package/dist/tests/test-prep-issue.js +0 -129
- package/dist/tests/test-productivity-integration.js +0 -157
- package/dist/tests/test-script-location-independence.js +0 -198
- package/dist/tests/test-script-sync.js +0 -557
- package/dist/tests/test-server-utils.js +0 -32
- package/dist/tests/test-session-rehydration.js +0 -148
- package/dist/tests/test-setup-integration.js +0 -98
- package/dist/tests/test-setup-scenarios.js +0 -322
- package/dist/tests/test-standalone.js +0 -143
- package/dist/tests/test-stub-registry.js +0 -136
- package/dist/tests/test-sync-stubs.js +0 -143
- package/dist/tests/test-sync-version-update.js +0 -93
- package/dist/tests/test-telemetry.js +0 -193
- package/dist/tests/test-token-validator.js +0 -30
- package/dist/tests/test-user-journey.js +0 -236
- package/dist/tests/test-users-to-target-workflow.js +0 -253
- package/dist/tests/test-utils.js +0 -109
- package/dist/tests/test-wizard.js +0 -71
- package/dist/tests/test-workflow-discovery.js +0 -242
- package/labels.json +0 -52
- package/registry/agent-guardrails.md +0 -63
- package/registry/fraim.md +0 -48
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase1-customer-profiling.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase1-survey-scoping.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase2-platform-discovery.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase2-survey-build-linkedin.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase3-prospect-qualification.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase3-survey-build-reddit.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase4-inventory-compilation.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase4-survey-build-x.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase5-survey-build-facebook.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase6-survey-build-custom.md +0 -11
- package/registry/stubs/workflows/customer-development/ai-coach-phases/phase7-survey-dispatch.md +0 -11
- package/registry/stubs/workflows/customer-development/templates/customer-persona-template.md +0 -11
- package/registry/stubs/workflows/customer-development/templates/search-strategy-template.md +0 -11
- package/setup.js +0 -171
- package/tsconfig.json +0 -23
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const node_child_process_1 = require("node:child_process");
|
|
7
|
-
const test_utils_1 = require("./test-utils");
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const os_1 = __importDefault(require("os"));
|
|
11
|
-
async function testInitOnNodeVersion(version) {
|
|
12
|
-
console.log(` ๐งช Testing Node v${version}...`);
|
|
13
|
-
// Create a temp directory for each version test
|
|
14
|
-
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `fraim-node-${version}-`));
|
|
15
|
-
try {
|
|
16
|
-
// Set up git repository (required for new init logic)
|
|
17
|
-
const { execSync } = require('child_process');
|
|
18
|
-
execSync('git init', { cwd: tempDir });
|
|
19
|
-
execSync('git remote add origin https://github.com/test-owner/test-repo.git', { cwd: tempDir });
|
|
20
|
-
const platform = process.platform;
|
|
21
|
-
const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
22
|
-
const binPath = (0, test_utils_1.resolveProjectPath)('dist/src/cli/fraim.js');
|
|
23
|
-
// Run: npx -y -p node@<version> node bin/fraim.js init
|
|
24
|
-
// We use -y to skip prompt, and -p node@version to ensure that specific node binary is used
|
|
25
|
-
const execNode = (args) => {
|
|
26
|
-
return new Promise((resolve) => {
|
|
27
|
-
const ps = (0, node_child_process_1.spawn)(npx, ['-y', '-p', `node@${version}`, 'node', `"${binPath}"`, ...args], {
|
|
28
|
-
cwd: tempDir,
|
|
29
|
-
env: { ...process.env, TEST_MODE: 'true' },
|
|
30
|
-
shell: true
|
|
31
|
-
});
|
|
32
|
-
let stdout = '';
|
|
33
|
-
let stderr = '';
|
|
34
|
-
let timedOut = false;
|
|
35
|
-
// Set a timeout of 30 seconds for npx to download and run
|
|
36
|
-
const timeout = setTimeout(() => {
|
|
37
|
-
timedOut = true;
|
|
38
|
-
ps.kill();
|
|
39
|
-
}, 30000);
|
|
40
|
-
ps.stdout.on('data', d => stdout += d.toString());
|
|
41
|
-
ps.stderr.on('data', d => stderr += d.toString());
|
|
42
|
-
ps.on('close', (code) => {
|
|
43
|
-
clearTimeout(timeout);
|
|
44
|
-
resolve({ stdout, stderr, code, timedOut });
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
};
|
|
48
|
-
console.log(` Running "fraim init" on Node ${version}...`);
|
|
49
|
-
const result = await execNode(['init']);
|
|
50
|
-
if (result.timedOut) {
|
|
51
|
-
console.log(` โ ๏ธ Skipped Node ${version} (timeout - version may not be available)`);
|
|
52
|
-
return true; // Don't fail the test, just skip it
|
|
53
|
-
}
|
|
54
|
-
if (result.code !== 0) {
|
|
55
|
-
// Check if it's a version availability issue
|
|
56
|
-
if (result.stderr.includes('ETARGET') || result.stderr.includes('No matching version')) {
|
|
57
|
-
console.log(` โ ๏ธ Skipped Node ${version} (version not available)`);
|
|
58
|
-
return true; // Don't fail the test
|
|
59
|
-
}
|
|
60
|
-
console.error(` โ Failed on Node ${version}`);
|
|
61
|
-
console.error(` Error: ${result.stderr}`);
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
const configPath = path_1.default.join(tempDir, '.fraim', 'config.json');
|
|
65
|
-
if (fs_1.default.existsSync(configPath)) {
|
|
66
|
-
console.log(` โ
Success on Node ${version}`);
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
console.error(` โ Config not found on Node ${version}`);
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
catch (e) {
|
|
75
|
-
console.error(` โ Test Logic Error on Node ${version}:`, e.message);
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
finally {
|
|
79
|
-
try {
|
|
80
|
-
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
81
|
-
}
|
|
82
|
-
catch (e) { }
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
const nodeVersions = ['16', '18', '20', '22'];
|
|
86
|
-
const testCases = nodeVersions.map(v => ({
|
|
87
|
-
name: `Node v${v} Compatibility`,
|
|
88
|
-
version: v,
|
|
89
|
-
tags: ['node', 'compatibility']
|
|
90
|
-
}));
|
|
91
|
-
(0, test_utils_1.runTests)(testCases, async (t) => await testInitOnNodeVersion(t.version), 'Node Compatibility Matrix')
|
|
92
|
-
.catch((err) => {
|
|
93
|
-
console.error('Test runner failed:', err);
|
|
94
|
-
throw err;
|
|
95
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const node_child_process_1 = require("node:child_process");
|
|
7
|
-
const test_utils_1 = require("./test-utils");
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const os_1 = __importDefault(require("os"));
|
|
11
|
-
async function testCleanNpmInstall(version) {
|
|
12
|
-
console.log(` ๐งช Testing NPM Install on Node v${version}...`);
|
|
13
|
-
// Create a temp directory for the install test
|
|
14
|
-
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `fraim-npm-${version}-`));
|
|
15
|
-
try {
|
|
16
|
-
const platform = process.platform;
|
|
17
|
-
const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
18
|
-
// Copy package.json to temp dir
|
|
19
|
-
const packageJsonPath = path_1.default.resolve(__dirname, '../package.json');
|
|
20
|
-
fs_1.default.copyFileSync(packageJsonPath, path_1.default.join(tempDir, 'package.json'));
|
|
21
|
-
// Run: npx -p node@<version> npm install --package-lock-only
|
|
22
|
-
// This uses the npm bundled with that specific Node version
|
|
23
|
-
const execNpm = () => {
|
|
24
|
-
return new Promise((resolve) => {
|
|
25
|
-
const ps = (0, node_child_process_1.spawn)(npx, ['-y', '-p', `node@${version}`, 'npm', 'install', '--package-lock-only', '--quiet', '--no-audit'], {
|
|
26
|
-
cwd: tempDir,
|
|
27
|
-
env: { ...process.env },
|
|
28
|
-
shell: true
|
|
29
|
-
});
|
|
30
|
-
let stdout = '';
|
|
31
|
-
let stderr = '';
|
|
32
|
-
ps.stdout.on('data', d => stdout += d.toString());
|
|
33
|
-
ps.stderr.on('data', d => stderr += d.toString());
|
|
34
|
-
ps.on('close', (code) => resolve({ stdout, stderr, code }));
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
console.log(` Running "npm install" with Node ${version}...`);
|
|
38
|
-
const result = await execNpm();
|
|
39
|
-
if (result.code !== 0) {
|
|
40
|
-
console.error(` โ NPM Install Failed on Node ${version}`);
|
|
41
|
-
console.error(` Stderr: ${result.stderr}`);
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
console.log(` โ
NPM Resolution Success on Node ${version}`);
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
catch (e) {
|
|
48
|
-
console.error(` โ Test Logic Error on Node ${version}:`, e.message);
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
finally {
|
|
52
|
-
try {
|
|
53
|
-
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
54
|
-
}
|
|
55
|
-
catch (e) { }
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const nodeVersions = ['16', '18', '20', '22'];
|
|
59
|
-
const testCases = nodeVersions.map(v => ({
|
|
60
|
-
name: `NPM Compatibility (Node v${v})`,
|
|
61
|
-
version: v,
|
|
62
|
-
tags: ['npm', 'compatibility']
|
|
63
|
-
}));
|
|
64
|
-
(0, test_utils_1.runTests)(testCases, async (t) => await testCleanNpmInstall(t.version), 'NPM Configuration Matrix')
|
|
65
|
-
.catch((err) => {
|
|
66
|
-
console.error('Test runner failed:', err);
|
|
67
|
-
throw err;
|
|
68
|
-
});
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
"use strict";
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const node_test_1 = require("node:test");
|
|
8
|
-
const node_assert_1 = __importDefault(require("node:assert"));
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
|
-
/**
|
|
12
|
-
* Test to verify package size reduction from stub-only registry
|
|
13
|
-
*/
|
|
14
|
-
// Find project root by looking for package.json
|
|
15
|
-
function findProjectRoot() {
|
|
16
|
-
let currentDir = __dirname;
|
|
17
|
-
while (currentDir !== path_1.default.dirname(currentDir)) {
|
|
18
|
-
if (fs_1.default.existsSync(path_1.default.join(currentDir, 'package.json'))) {
|
|
19
|
-
return currentDir;
|
|
20
|
-
}
|
|
21
|
-
currentDir = path_1.default.dirname(currentDir);
|
|
22
|
-
}
|
|
23
|
-
throw new Error('Could not find project root (package.json not found)');
|
|
24
|
-
}
|
|
25
|
-
(0, node_test_1.test)('Stub registry is significantly smaller than full registry', () => {
|
|
26
|
-
const projectRoot = findProjectRoot();
|
|
27
|
-
const fullWorkflowsPath = path_1.default.join(projectRoot, 'registry', 'workflows');
|
|
28
|
-
const stubWorkflowsPath = path_1.default.join(projectRoot, 'registry', 'stubs', 'workflows');
|
|
29
|
-
console.log(`Project root: ${projectRoot}`);
|
|
30
|
-
console.log(`Full workflows path: ${fullWorkflowsPath}`);
|
|
31
|
-
console.log(`Stub workflows path: ${stubWorkflowsPath}`);
|
|
32
|
-
(0, node_assert_1.default)(fs_1.default.existsSync(fullWorkflowsPath), 'Full workflows directory should exist');
|
|
33
|
-
(0, node_assert_1.default)(fs_1.default.existsSync(stubWorkflowsPath), 'Stub workflows directory should exist');
|
|
34
|
-
// Calculate sizes
|
|
35
|
-
const fullSize = getDirectorySize(fullWorkflowsPath);
|
|
36
|
-
const stubSize = getDirectorySize(stubWorkflowsPath);
|
|
37
|
-
console.log(`Full workflows size: ${fullSize} bytes`);
|
|
38
|
-
console.log(`Stub workflows size: ${stubSize} bytes`);
|
|
39
|
-
console.log(`Size reduction: ${Math.round((1 - stubSize / fullSize) * 100)}%`);
|
|
40
|
-
// Stubs should be significantly smaller
|
|
41
|
-
(0, node_assert_1.default)(stubSize < fullSize * 0.3, `Stubs should be <30% of full size (actual: ${Math.round(stubSize / fullSize * 100)}%)`);
|
|
42
|
-
// Should have same number of files
|
|
43
|
-
const fullFiles = getAllFiles(fullWorkflowsPath, '.md');
|
|
44
|
-
const stubFiles = getAllFiles(stubWorkflowsPath, '.md');
|
|
45
|
-
console.log(`Full files found: ${fullFiles.length}`);
|
|
46
|
-
console.log(`Stub files found: ${stubFiles.length}`);
|
|
47
|
-
console.log(`First few full files: ${fullFiles.slice(0, 3).join(', ')}`);
|
|
48
|
-
console.log(`First few stub files: ${stubFiles.slice(0, 3).join(', ')}`);
|
|
49
|
-
node_assert_1.default.strictEqual(stubFiles.length, fullFiles.length, 'Should have same number of workflow files');
|
|
50
|
-
console.log(`โ
Package size reduced by ${Math.round((1 - stubSize / fullSize) * 100)}% while maintaining all ${stubFiles.length} workflows`);
|
|
51
|
-
});
|
|
52
|
-
(0, node_test_1.test)('Package excludes full workflows and templates', () => {
|
|
53
|
-
const projectRoot = findProjectRoot();
|
|
54
|
-
const packageJsonPath = path_1.default.join(projectRoot, 'package.json');
|
|
55
|
-
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
|
|
56
|
-
const files = packageJson.files || [];
|
|
57
|
-
// Calculate what would be excluded
|
|
58
|
-
const fullWorkflowsSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'workflows'));
|
|
59
|
-
const templatesSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'templates'));
|
|
60
|
-
const rulesSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'rules'));
|
|
61
|
-
const excludedSize = fullWorkflowsSize + templatesSize + rulesSize;
|
|
62
|
-
// What's included
|
|
63
|
-
const stubsSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'stubs'));
|
|
64
|
-
const scriptsSize = getDirectorySize(path_1.default.join(projectRoot, 'registry', 'scripts'));
|
|
65
|
-
const includedSize = stubsSize + scriptsSize;
|
|
66
|
-
console.log(`Excluded from package: ${excludedSize} bytes (workflows + templates + rules)`);
|
|
67
|
-
console.log(`Included in package: ${includedSize} bytes (stubs + scripts)`);
|
|
68
|
-
console.log(`Total registry size reduction: ${Math.round((1 - includedSize / (includedSize + excludedSize)) * 100)}%`);
|
|
69
|
-
// Verify significant size reduction
|
|
70
|
-
(0, node_assert_1.default)(includedSize < excludedSize, 'Package should exclude more content than it includes');
|
|
71
|
-
console.log('โ
Package correctly excludes heavy content while keeping essentials');
|
|
72
|
-
});
|
|
73
|
-
function getDirectorySize(dirPath) {
|
|
74
|
-
if (!fs_1.default.existsSync(dirPath)) {
|
|
75
|
-
return 0;
|
|
76
|
-
}
|
|
77
|
-
let totalSize = 0;
|
|
78
|
-
const files = getAllFiles(dirPath);
|
|
79
|
-
for (const file of files) {
|
|
80
|
-
try {
|
|
81
|
-
const stats = fs_1.default.statSync(file);
|
|
82
|
-
totalSize += stats.size;
|
|
83
|
-
}
|
|
84
|
-
catch (e) {
|
|
85
|
-
// Skip files that can't be read
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return totalSize;
|
|
89
|
-
}
|
|
90
|
-
function getAllFiles(dir, extension) {
|
|
91
|
-
const files = [];
|
|
92
|
-
if (!fs_1.default.existsSync(dir)) {
|
|
93
|
-
return files;
|
|
94
|
-
}
|
|
95
|
-
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
96
|
-
for (const entry of entries) {
|
|
97
|
-
const fullPath = path_1.default.join(dir, entry.name);
|
|
98
|
-
if (entry.isDirectory()) {
|
|
99
|
-
files.push(...getAllFiles(fullPath, extension));
|
|
100
|
-
}
|
|
101
|
-
else if (entry.isFile()) {
|
|
102
|
-
if (!extension || entry.name.endsWith(extension)) {
|
|
103
|
-
files.push(fullPath);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return files;
|
|
108
|
-
}
|
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const node_assert_1 = require("node:assert");
|
|
4
|
-
const test_utils_1 = require("./test-utils");
|
|
5
|
-
const phase_flow_js_1 = require("../src/ai-manager/phase-flow.js");
|
|
6
|
-
const fs_1 = require("fs");
|
|
7
|
-
const path_1 = require("path");
|
|
8
|
-
/**
|
|
9
|
-
* Test PR Review Workflow Phase Flow
|
|
10
|
-
*/
|
|
11
|
-
async function testPRReviewPhaseFlow() {
|
|
12
|
-
try {
|
|
13
|
-
console.log('๐งช Testing PR review phase flow...');
|
|
14
|
-
// Test bug workflow includes new phases
|
|
15
|
-
const bugPhases = (0, phase_flow_js_1.getPhasesForWorkflow)('implement', 'bug');
|
|
16
|
-
(0, node_assert_1.strict)(bugPhases.includes('submit-pr'), 'Bug workflow should include submit-pr phase');
|
|
17
|
-
(0, node_assert_1.strict)(bugPhases.includes('wait-for-pr-review'), 'Bug workflow should include wait-for-pr-review phase');
|
|
18
|
-
// Test feature workflow includes new phases
|
|
19
|
-
const featurePhases = (0, phase_flow_js_1.getPhasesForWorkflow)('implement', 'feature');
|
|
20
|
-
(0, node_assert_1.strict)(featurePhases.includes('submit-pr'), 'Feature workflow should include submit-pr phase');
|
|
21
|
-
(0, node_assert_1.strict)(featurePhases.includes('wait-for-pr-review'), 'Feature workflow should include wait-for-pr-review phase');
|
|
22
|
-
// Test phase progression
|
|
23
|
-
const nextAfterCompleteness = (0, phase_flow_js_1.getNextPhase)('implement-completeness-review', 'implement', 'bug');
|
|
24
|
-
node_assert_1.strict.equal(nextAfterCompleteness, 'submit-pr', 'After completeness-review should go to submit-pr');
|
|
25
|
-
const nextAfterSubmitPR = (0, phase_flow_js_1.getNextPhase)('submit-pr', 'implement', 'bug');
|
|
26
|
-
node_assert_1.strict.equal(nextAfterSubmitPR, 'wait-for-pr-review', 'After submit-pr should go to wait-for-pr-review');
|
|
27
|
-
const nextAfterWaitPR = (0, phase_flow_js_1.getNextPhase)('wait-for-pr-review', 'implement', 'bug');
|
|
28
|
-
node_assert_1.strict.equal(nextAfterWaitPR, 'retrospective', 'After wait-for-pr-review should go to retrospective');
|
|
29
|
-
const nextAfterRetrospective = (0, phase_flow_js_1.getNextPhase)('retrospective', 'implement', 'bug');
|
|
30
|
-
node_assert_1.strict.equal(nextAfterRetrospective, null, 'retrospective should be the last phase');
|
|
31
|
-
console.log('โ
PR review phase flow tests passed');
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
console.error('โ PR review phase flow test failed:', error.message);
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Test PR Review Failure Handling
|
|
41
|
-
*/
|
|
42
|
-
async function testPRReviewFailureHandling() {
|
|
43
|
-
try {
|
|
44
|
-
console.log('๐งช Testing PR review failure handling...');
|
|
45
|
-
// Test submit-pr failure
|
|
46
|
-
const submitPRFailure = (0, phase_flow_js_1.getPhaseOnFailure)('submit-pr', 'implement', 'bug');
|
|
47
|
-
node_assert_1.strict.equal(submitPRFailure, 'implement-completeness-review', 'submit-pr failure should return to completeness-review');
|
|
48
|
-
// Test wait-for-pr-review failure (should go to address-pr-feedback)
|
|
49
|
-
const waitPRFailure = (0, phase_flow_js_1.getPhaseOnFailure)('wait-for-pr-review', 'implement', 'bug');
|
|
50
|
-
node_assert_1.strict.equal(waitPRFailure, 'address-pr-feedback', 'wait-for-pr-review failure should go to address-pr-feedback');
|
|
51
|
-
// Test all workflow types have new phases
|
|
52
|
-
const specPhases = (0, phase_flow_js_1.getPhasesForWorkflow)('spec');
|
|
53
|
-
(0, node_assert_1.strict)(specPhases.includes('submit-pr'), 'Spec workflow should include submit-pr');
|
|
54
|
-
(0, node_assert_1.strict)(specPhases.includes('wait-for-pr-review'), 'Spec workflow should include wait-for-pr-review');
|
|
55
|
-
const designPhases = (0, phase_flow_js_1.getPhasesForWorkflow)('design');
|
|
56
|
-
(0, node_assert_1.strict)(designPhases.includes('submit-pr'), 'Design workflow should include submit-pr');
|
|
57
|
-
(0, node_assert_1.strict)(designPhases.includes('wait-for-pr-review'), 'Design workflow should include wait-for-pr-review');
|
|
58
|
-
console.log('โ
PR review failure handling tests passed');
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
console.error('โ PR review failure handling test failed:', error.message);
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Test PR Comment Polling Script (Mock)
|
|
68
|
-
*/
|
|
69
|
-
async function testPRCommentPollingScript() {
|
|
70
|
-
try {
|
|
71
|
-
console.log('๐งช Testing PR comment polling script...');
|
|
72
|
-
// Create mock .fraim/config.json
|
|
73
|
-
const testDir = (0, path_1.join)(process.cwd(), 'test_temp');
|
|
74
|
-
const fraimDir = (0, path_1.join)(testDir, '.fraim');
|
|
75
|
-
if ((0, fs_1.existsSync)(testDir)) {
|
|
76
|
-
(0, fs_1.rmSync)(testDir, { recursive: true, force: true });
|
|
77
|
-
}
|
|
78
|
-
(0, fs_1.mkdirSync)(fraimDir, { recursive: true });
|
|
79
|
-
const mockConfig = {
|
|
80
|
-
git: {
|
|
81
|
-
repoOwner: 'test-owner',
|
|
82
|
-
repoName: 'test-repo'
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
(0, fs_1.writeFileSync)((0, path_1.join)(fraimDir, 'config.json'), JSON.stringify(mockConfig, null, 2));
|
|
86
|
-
// Test script exists and is executable
|
|
87
|
-
const scriptPath = (0, path_1.join)(process.cwd(), 'scripts', 'sleep-and-continue.js');
|
|
88
|
-
(0, node_assert_1.strict)((0, fs_1.existsSync)(scriptPath), 'Sleep utility script should exist');
|
|
89
|
-
// Test script can be executed (syntax check)
|
|
90
|
-
const { execSync } = require('child_process');
|
|
91
|
-
try {
|
|
92
|
-
// Test help output
|
|
93
|
-
execSync('node scripts/sleep-and-continue.js --help', { stdio: 'pipe' });
|
|
94
|
-
}
|
|
95
|
-
catch (error) {
|
|
96
|
-
// Expected to fail with usage message, which is fine
|
|
97
|
-
(0, node_assert_1.strict)(error.message.includes('Usage:'), 'Script should show usage when called incorrectly');
|
|
98
|
-
}
|
|
99
|
-
// Cleanup
|
|
100
|
-
(0, fs_1.rmSync)(testDir, { recursive: true, force: true });
|
|
101
|
-
console.log('โ
PR comment polling script tests passed');
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
console.error('โ PR comment polling script test failed:', error.message);
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Test PR Feedback File System
|
|
111
|
-
*/
|
|
112
|
-
async function testPRCommentVerificationScript() {
|
|
113
|
-
try {
|
|
114
|
-
console.log('๐งช Testing PR feedback file system...');
|
|
115
|
-
const testDir = (0, path_1.join)(process.cwd(), 'test_temp');
|
|
116
|
-
const evidenceDir = (0, path_1.join)(testDir, 'evidence');
|
|
117
|
-
if ((0, fs_1.existsSync)(testDir)) {
|
|
118
|
-
(0, fs_1.rmSync)(testDir, { recursive: true, force: true });
|
|
119
|
-
}
|
|
120
|
-
(0, fs_1.mkdirSync)(evidenceDir, { recursive: true });
|
|
121
|
-
// Create mock feedback document using new format
|
|
122
|
-
const mockFeedbackDoc = `# PR Feedback for Issue #123 - Implement Workflow
|
|
123
|
-
|
|
124
|
-
## Round 1 Feedback
|
|
125
|
-
*Received: 2024-01-15T10:30:00Z*
|
|
126
|
-
|
|
127
|
-
### Comment 1 - ADDRESSED
|
|
128
|
-
- **Author**: reviewer1
|
|
129
|
-
- **Type**: review_comment
|
|
130
|
-
- **File**: src/test.ts
|
|
131
|
-
- **Line**: 45
|
|
132
|
-
- **Comment**: This function needs error handling
|
|
133
|
-
- **Status**: ADDRESSED
|
|
134
|
-
- **Resolution**: Added try-catch block and proper error handling
|
|
135
|
-
|
|
136
|
-
### Comment 2 - ADDRESSED
|
|
137
|
-
- **Author**: reviewer2
|
|
138
|
-
- **Type**: pr_comment
|
|
139
|
-
- **Comment**: Please add unit tests for this feature
|
|
140
|
-
- **Status**: ADDRESSED
|
|
141
|
-
- **Resolution**: Added comprehensive unit tests in test/feature.test.ts
|
|
142
|
-
`;
|
|
143
|
-
const feedbackDocPath = (0, path_1.join)(evidenceDir, '123-implement-feedback.md');
|
|
144
|
-
(0, fs_1.writeFileSync)(feedbackDocPath, mockFeedbackDoc);
|
|
145
|
-
// Test that feedback document was created and has expected content
|
|
146
|
-
(0, node_assert_1.strict)((0, fs_1.existsSync)(feedbackDocPath), 'PR feedback document should exist');
|
|
147
|
-
const content = (0, fs_1.readFileSync)(feedbackDocPath, 'utf-8');
|
|
148
|
-
(0, node_assert_1.strict)(content.includes('## Round 1 Feedback'), 'Document should contain Round 1 Feedback');
|
|
149
|
-
(0, node_assert_1.strict)(content.includes('### Comment 1 - ADDRESSED'), 'Document should contain Comment 1 with status');
|
|
150
|
-
(0, node_assert_1.strict)(content.includes('### Comment 2 - ADDRESSED'), 'Document should contain Comment 2 with status');
|
|
151
|
-
(0, node_assert_1.strict)(content.includes('reviewer1'), 'Document should contain reviewer1');
|
|
152
|
-
(0, node_assert_1.strict)(content.includes('reviewer2'), 'Document should contain reviewer2');
|
|
153
|
-
(0, node_assert_1.strict)(content.includes('Status**: ADDRESSED'), 'Comments should be marked as ADDRESSED');
|
|
154
|
-
// Cleanup
|
|
155
|
-
(0, fs_1.rmSync)(testDir, { recursive: true, force: true });
|
|
156
|
-
console.log('โ
PR feedback file system tests passed');
|
|
157
|
-
return true;
|
|
158
|
-
}
|
|
159
|
-
catch (error) {
|
|
160
|
-
console.error('โ PR feedback file system test failed:', error.message);
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Test Real-World PR Review Scenario
|
|
166
|
-
*/
|
|
167
|
-
async function testRealWorldPRReviewScenario() {
|
|
168
|
-
try {
|
|
169
|
-
console.log('๐งช Testing real-world PR review scenario...');
|
|
170
|
-
// Scenario: Feature implementation with PR feedback
|
|
171
|
-
let currentPhase = 'implement-scoping';
|
|
172
|
-
const workflowType = 'implement';
|
|
173
|
-
const issueType = 'feature';
|
|
174
|
-
// Progress through normal phases
|
|
175
|
-
const expectedFlow = [
|
|
176
|
-
'implement-scoping',
|
|
177
|
-
'implement-spike',
|
|
178
|
-
'implement-code',
|
|
179
|
-
'implement-validate',
|
|
180
|
-
'implement-smoke',
|
|
181
|
-
'implement-regression',
|
|
182
|
-
'implement-completeness-review',
|
|
183
|
-
'submit-pr',
|
|
184
|
-
'wait-for-pr-review'
|
|
185
|
-
];
|
|
186
|
-
for (let i = 0; i < expectedFlow.length - 1; i++) {
|
|
187
|
-
const nextPhase = (0, phase_flow_js_1.getNextPhase)(currentPhase, workflowType, issueType);
|
|
188
|
-
node_assert_1.strict.equal(nextPhase, expectedFlow[i + 1], `After ${currentPhase} should go to ${expectedFlow[i + 1]}`);
|
|
189
|
-
currentPhase = nextPhase;
|
|
190
|
-
}
|
|
191
|
-
// Test PR review completion (approved)
|
|
192
|
-
const nextPhaseAfterPR = (0, phase_flow_js_1.getNextPhase)('wait-for-pr-review', workflowType, issueType);
|
|
193
|
-
node_assert_1.strict.equal(nextPhaseAfterPR, 'retrospective', 'wait-for-pr-review should go to retrospective when approved');
|
|
194
|
-
const finalPhase = (0, phase_flow_js_1.getNextPhase)('retrospective', workflowType, issueType);
|
|
195
|
-
node_assert_1.strict.equal(finalPhase, null, 'retrospective should be final phase when approved');
|
|
196
|
-
// Test PR review with changes requested (failure scenario)
|
|
197
|
-
const failurePhase = (0, phase_flow_js_1.getPhaseOnFailure)('wait-for-pr-review', workflowType, issueType);
|
|
198
|
-
node_assert_1.strict.equal(failurePhase, 'address-pr-feedback', 'PR changes requested should route to address-pr-feedback');
|
|
199
|
-
// Test iteration: after addressing feedback, go through validation again
|
|
200
|
-
let iterationPhase = 'address-pr-feedback';
|
|
201
|
-
// After addressing feedback, should go to validation phase
|
|
202
|
-
const nextAfterFeedback = (0, phase_flow_js_1.getNextPhase)(iterationPhase, workflowType, issueType);
|
|
203
|
-
node_assert_1.strict.equal(nextAfterFeedback, 'implement-validate', 'After address-pr-feedback should go to implement-validate');
|
|
204
|
-
iterationPhase = nextAfterFeedback;
|
|
205
|
-
const iterationFlow = ['implement-smoke', 'implement-regression', 'implement-completeness-review', 'submit-pr', 'wait-for-pr-review'];
|
|
206
|
-
for (const expectedNext of iterationFlow) {
|
|
207
|
-
const nextPhase = (0, phase_flow_js_1.getNextPhase)(iterationPhase, workflowType, issueType);
|
|
208
|
-
node_assert_1.strict.equal(nextPhase, expectedNext, `Iteration: after ${iterationPhase} should go to ${expectedNext}`);
|
|
209
|
-
iterationPhase = nextPhase;
|
|
210
|
-
}
|
|
211
|
-
console.log('โ
Real-world PR review scenario tests passed');
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
catch (error) {
|
|
215
|
-
console.error('โ Real-world PR review scenario test failed:', error.message);
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Test PR Comment Routing Logic
|
|
221
|
-
*/
|
|
222
|
-
async function testPRCommentRoutingLogic() {
|
|
223
|
-
try {
|
|
224
|
-
console.log('๐งช Testing PR comment routing logic...');
|
|
225
|
-
// Test different comment types should route to different phases
|
|
226
|
-
// This would be implemented in the wait-for-pr-review phase logic
|
|
227
|
-
// Mock comment analysis scenarios
|
|
228
|
-
const commentScenarios = [
|
|
229
|
-
{
|
|
230
|
-
comment: "This doesn't match the requirements in the spec",
|
|
231
|
-
expectedRoute: 'implement-scoping', // or spec-spec for spec workflow
|
|
232
|
-
category: 'requirements'
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
comment: "The API design doesn't follow our patterns",
|
|
236
|
-
expectedRoute: 'design-design',
|
|
237
|
-
category: 'design'
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
comment: "This code has a memory leak",
|
|
241
|
-
expectedRoute: 'implement-code',
|
|
242
|
-
category: 'code_quality'
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
comment: "Missing test coverage for edge cases",
|
|
246
|
-
expectedRoute: 'implement-validate',
|
|
247
|
-
category: 'testing'
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
comment: "The documentation is incomplete",
|
|
251
|
-
expectedRoute: 'implement-completeness-review',
|
|
252
|
-
category: 'documentation'
|
|
253
|
-
}
|
|
254
|
-
];
|
|
255
|
-
// For now, just verify the routing logic exists in the failure handling
|
|
256
|
-
for (const scenario of commentScenarios) {
|
|
257
|
-
// The actual routing would be implemented in the wait-for-pr-review phase
|
|
258
|
-
// For now, we test that the failure handling provides reasonable defaults
|
|
259
|
-
const routePhase = (0, phase_flow_js_1.getPhaseOnFailure)('wait-for-pr-review', 'implement', 'feature');
|
|
260
|
-
(0, node_assert_1.strict)(typeof routePhase === 'string', `Should return a valid phase for routing`);
|
|
261
|
-
}
|
|
262
|
-
console.log('โ
PR comment routing logic tests passed');
|
|
263
|
-
return true;
|
|
264
|
-
}
|
|
265
|
-
catch (error) {
|
|
266
|
-
console.error('โ PR comment routing logic test failed:', error.message);
|
|
267
|
-
return false;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Test Phase Validation
|
|
272
|
-
*/
|
|
273
|
-
async function testPhaseValidation() {
|
|
274
|
-
try {
|
|
275
|
-
console.log('๐งช Testing phase validation...');
|
|
276
|
-
// Test new phases are valid for all workflow types
|
|
277
|
-
(0, node_assert_1.strict)((0, phase_flow_js_1.isPhaseValidForWorkflow)('submit-pr', 'implement', 'bug'), 'submit-pr should be valid for implement workflow');
|
|
278
|
-
(0, node_assert_1.strict)((0, phase_flow_js_1.isPhaseValidForWorkflow)('wait-for-pr-review', 'implement', 'bug'), 'wait-for-pr-review should be valid for implement workflow');
|
|
279
|
-
(0, node_assert_1.strict)((0, phase_flow_js_1.isPhaseValidForWorkflow)('submit-pr', 'spec'), 'submit-pr should be valid for spec workflow');
|
|
280
|
-
(0, node_assert_1.strict)((0, phase_flow_js_1.isPhaseValidForWorkflow)('wait-for-pr-review', 'spec'), 'wait-for-pr-review should be valid for spec workflow');
|
|
281
|
-
(0, node_assert_1.strict)((0, phase_flow_js_1.isPhaseValidForWorkflow)('submit-pr', 'design'), 'submit-pr should be valid for design workflow');
|
|
282
|
-
(0, node_assert_1.strict)((0, phase_flow_js_1.isPhaseValidForWorkflow)('wait-for-pr-review', 'design'), 'wait-for-pr-review should be valid for design workflow');
|
|
283
|
-
(0, node_assert_1.strict)((0, phase_flow_js_1.isPhaseValidForWorkflow)('submit-pr', 'test'), 'submit-pr should be valid for test workflow');
|
|
284
|
-
(0, node_assert_1.strict)((0, phase_flow_js_1.isPhaseValidForWorkflow)('wait-for-pr-review', 'test'), 'wait-for-pr-review should be valid for test workflow');
|
|
285
|
-
// Test old finalize phase is no longer valid
|
|
286
|
-
(0, node_assert_1.strict)(!(0, phase_flow_js_1.isPhaseValidForWorkflow)('finalize', 'implement', 'bug'), 'finalize should no longer be valid for implement workflow');
|
|
287
|
-
(0, node_assert_1.strict)(!(0, phase_flow_js_1.isPhaseValidForWorkflow)('finalize', 'spec'), 'finalize should no longer be valid for spec workflow');
|
|
288
|
-
console.log('โ
Phase validation tests passed');
|
|
289
|
-
return true;
|
|
290
|
-
}
|
|
291
|
-
catch (error) {
|
|
292
|
-
console.error('โ Phase validation test failed:', error.message);
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
// Test cases
|
|
297
|
-
const testCases = [
|
|
298
|
-
{ name: 'PR Review Phase Flow', testFn: testPRReviewPhaseFlow },
|
|
299
|
-
{ name: 'PR Review Failure Handling', testFn: testPRReviewFailureHandling },
|
|
300
|
-
{ name: 'PR Comment Polling Script', testFn: testPRCommentPollingScript },
|
|
301
|
-
{ name: 'PR Feedback File System', testFn: testPRCommentVerificationScript },
|
|
302
|
-
{ name: 'Real-World PR Review Scenario', testFn: testRealWorldPRReviewScenario },
|
|
303
|
-
{ name: 'PR Comment Routing Logic', testFn: testPRCommentRoutingLogic },
|
|
304
|
-
{ name: 'Phase Validation', testFn: testPhaseValidation }
|
|
305
|
-
];
|
|
306
|
-
// Run tests
|
|
307
|
-
(0, test_utils_1.runTests)(testCases, async (t) => t.testFn(), 'PR Review Workflow Tests');
|