gswd 1.0.0 → 1.1.0
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/bin/gswd-tools.cjs +228 -0
- package/bin/install.js +8 -0
- package/commands/gswd/imagine.md +7 -1
- package/commands/gswd/start.md +507 -32
- package/dist/lib/audit.d.ts +205 -0
- package/dist/lib/audit.js +805 -0
- package/dist/lib/bootstrap.d.ts +103 -0
- package/dist/lib/bootstrap.js +563 -0
- package/dist/lib/compile.d.ts +239 -0
- package/dist/lib/compile.js +1152 -0
- package/dist/lib/config.d.ts +49 -0
- package/dist/lib/config.js +150 -0
- package/dist/lib/imagine-agents.d.ts +54 -0
- package/dist/lib/imagine-agents.js +185 -0
- package/dist/lib/imagine-gate.d.ts +47 -0
- package/dist/lib/imagine-gate.js +131 -0
- package/dist/lib/imagine-input.d.ts +46 -0
- package/dist/lib/imagine-input.js +233 -0
- package/dist/lib/imagine-synthesis.d.ts +90 -0
- package/dist/lib/imagine-synthesis.js +453 -0
- package/dist/lib/imagine.d.ts +56 -0
- package/dist/lib/imagine.js +413 -0
- package/dist/lib/intake.d.ts +27 -0
- package/dist/lib/intake.js +82 -0
- package/dist/lib/parse.d.ts +59 -0
- package/dist/lib/parse.js +171 -0
- package/dist/lib/render.d.ts +309 -0
- package/dist/lib/render.js +624 -0
- package/dist/lib/specify-agents.d.ts +120 -0
- package/dist/lib/specify-agents.js +269 -0
- package/dist/lib/specify-journeys.d.ts +124 -0
- package/dist/lib/specify-journeys.js +279 -0
- package/dist/lib/specify-nfr.d.ts +45 -0
- package/dist/lib/specify-nfr.js +159 -0
- package/dist/lib/specify-roles.d.ts +46 -0
- package/dist/lib/specify-roles.js +88 -0
- package/dist/lib/specify.d.ts +70 -0
- package/dist/lib/specify.js +676 -0
- package/dist/lib/state.d.ts +140 -0
- package/dist/lib/state.js +340 -0
- package/dist/tests/audit.test.d.ts +4 -0
- package/dist/tests/audit.test.js +1579 -0
- package/dist/tests/bootstrap.test.d.ts +5 -0
- package/dist/tests/bootstrap.test.js +611 -0
- package/dist/tests/compile.test.d.ts +4 -0
- package/dist/tests/compile.test.js +862 -0
- package/dist/tests/config.test.d.ts +4 -0
- package/dist/tests/config.test.js +191 -0
- package/dist/tests/imagine-agents.test.d.ts +6 -0
- package/dist/tests/imagine-agents.test.js +179 -0
- package/dist/tests/imagine-gate.test.d.ts +6 -0
- package/dist/tests/imagine-gate.test.js +264 -0
- package/dist/tests/imagine-input.test.d.ts +6 -0
- package/dist/tests/imagine-input.test.js +283 -0
- package/dist/tests/imagine-synthesis.test.d.ts +7 -0
- package/dist/tests/imagine-synthesis.test.js +380 -0
- package/dist/tests/imagine.test.d.ts +8 -0
- package/dist/tests/imagine.test.js +406 -0
- package/dist/tests/parse.test.d.ts +4 -0
- package/dist/tests/parse.test.js +285 -0
- package/dist/tests/render.test.d.ts +4 -0
- package/dist/tests/render.test.js +236 -0
- package/dist/tests/specify-agents.test.d.ts +4 -0
- package/dist/tests/specify-agents.test.js +352 -0
- package/dist/tests/specify-journeys.test.d.ts +5 -0
- package/dist/tests/specify-journeys.test.js +440 -0
- package/dist/tests/specify-nfr.test.d.ts +4 -0
- package/dist/tests/specify-nfr.test.js +205 -0
- package/dist/tests/specify-roles.test.d.ts +4 -0
- package/dist/tests/specify-roles.test.js +136 -0
- package/dist/tests/specify.test.d.ts +9 -0
- package/dist/tests/specify.test.js +544 -0
- package/dist/tests/state.test.d.ts +4 -0
- package/dist/tests/state.test.js +316 -0
- package/lib/bootstrap.ts +37 -11
- package/lib/compile.ts +426 -4
- package/lib/imagine-agents.ts +53 -7
- package/lib/imagine-synthesis.ts +170 -6
- package/lib/imagine.ts +59 -5
- package/lib/intake.ts +60 -0
- package/lib/parse.ts +2 -1
- package/lib/render.ts +566 -5
- package/lib/specify-agents.ts +25 -3
- package/lib/state.ts +115 -0
- package/package.json +4 -2
- package/templates/gswd/DECISIONS.template.md +3 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Specify Agents module tests — Agent definitions, prompt building, orchestration, validation
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const node_test_1 = require("node:test");
|
|
40
|
+
const assert = __importStar(require("node:assert"));
|
|
41
|
+
const specify_agents_js_1 = require("../lib/specify-agents.js");
|
|
42
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
43
|
+
function makeContext(overrides) {
|
|
44
|
+
return {
|
|
45
|
+
decisionsContent: '## Frozen Decisions\n- Decision 1: Build a task manager',
|
|
46
|
+
imagineContent: '## Direction\nA simple task manager for solo founders',
|
|
47
|
+
journeys: [],
|
|
48
|
+
frs: [],
|
|
49
|
+
autoPolicy: 'balanced',
|
|
50
|
+
...overrides,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function makeSpawnFn(responses) {
|
|
54
|
+
return async (prompt) => {
|
|
55
|
+
for (const [key, value] of Object.entries(responses)) {
|
|
56
|
+
if (prompt.includes(key)) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return 'Default agent output';
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function makeFailingSpawnFn(failAgent) {
|
|
64
|
+
return async (prompt) => {
|
|
65
|
+
if (prompt.includes(failAgent)) {
|
|
66
|
+
throw new Error(`Agent ${failAgent} failed`);
|
|
67
|
+
}
|
|
68
|
+
return 'Success output';
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// ─── SPECIFY_AGENTS constant ────────────────────────────────────────────────
|
|
72
|
+
(0, node_test_1.describe)('SPECIFY_AGENTS', () => {
|
|
73
|
+
(0, node_test_1.it)('has exactly 3 agents', () => {
|
|
74
|
+
assert.strictEqual(specify_agents_js_1.SPECIFY_AGENTS.length, 3);
|
|
75
|
+
});
|
|
76
|
+
(0, node_test_1.it)('names match GSWD_SPEC: journey-mapper, architecture-drafter, integrations-checker', () => {
|
|
77
|
+
const names = specify_agents_js_1.SPECIFY_AGENTS.map((a) => a.name);
|
|
78
|
+
assert.ok(names.includes('journey-mapper'));
|
|
79
|
+
assert.ok(names.includes('architecture-drafter'));
|
|
80
|
+
assert.ok(names.includes('integrations-checker'));
|
|
81
|
+
});
|
|
82
|
+
(0, node_test_1.it)('journey-mapper is sequential, others are parallel', () => {
|
|
83
|
+
const journeyMapper = specify_agents_js_1.SPECIFY_AGENTS.find((a) => a.name === 'journey-mapper');
|
|
84
|
+
assert.strictEqual(journeyMapper.phase, 'sequential');
|
|
85
|
+
const archDrafter = specify_agents_js_1.SPECIFY_AGENTS.find((a) => a.name === 'architecture-drafter');
|
|
86
|
+
assert.strictEqual(archDrafter.phase, 'parallel');
|
|
87
|
+
const intChecker = specify_agents_js_1.SPECIFY_AGENTS.find((a) => a.name === 'integrations-checker');
|
|
88
|
+
assert.strictEqual(intChecker.phase, 'parallel');
|
|
89
|
+
});
|
|
90
|
+
(0, node_test_1.it)('each has a definition path in agents/gswd/', () => {
|
|
91
|
+
for (const agent of specify_agents_js_1.SPECIFY_AGENTS) {
|
|
92
|
+
assert.ok(agent.definitionPath.startsWith('agents/gswd/'), `${agent.name} path wrong`);
|
|
93
|
+
assert.ok(agent.definitionPath.endsWith('.md'), `${agent.name} path not .md`);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
(0, node_test_1.it)('each has at least one required heading', () => {
|
|
97
|
+
for (const agent of specify_agents_js_1.SPECIFY_AGENTS) {
|
|
98
|
+
assert.ok(agent.requiredHeadings.length > 0, `${agent.name} has no required headings`);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
// ─── buildSpecifyAgentPrompt ────────────────────────────────────────────────
|
|
103
|
+
(0, node_test_1.describe)('buildSpecifyAgentPrompt', () => {
|
|
104
|
+
(0, node_test_1.it)('journey-mapper prompt includes decisions content', () => {
|
|
105
|
+
const agent = specify_agents_js_1.SPECIFY_AGENTS.find((a) => a.name === 'journey-mapper');
|
|
106
|
+
const ctx = makeContext();
|
|
107
|
+
const prompt = (0, specify_agents_js_1.buildSpecifyAgentPrompt)(agent, ctx);
|
|
108
|
+
assert.ok(prompt.includes('Frozen Decisions'));
|
|
109
|
+
});
|
|
110
|
+
(0, node_test_1.it)('journey-mapper prompt includes imagine content', () => {
|
|
111
|
+
const agent = specify_agents_js_1.SPECIFY_AGENTS.find((a) => a.name === 'journey-mapper');
|
|
112
|
+
const ctx = makeContext({ imagineContent: 'A task manager for founders' });
|
|
113
|
+
const prompt = (0, specify_agents_js_1.buildSpecifyAgentPrompt)(agent, ctx);
|
|
114
|
+
assert.ok(prompt.includes('task manager for founders'));
|
|
115
|
+
});
|
|
116
|
+
(0, node_test_1.it)('architecture-drafter prompt includes FR list', () => {
|
|
117
|
+
const agent = specify_agents_js_1.SPECIFY_AGENTS.find((a) => a.name === 'architecture-drafter');
|
|
118
|
+
const ctx = makeContext({
|
|
119
|
+
frs: [
|
|
120
|
+
{ id: 'FR-001', description: 'Create task', scope: 'v1', priority: 'P0', sourceJourneys: ['J-001'], sourceSteps: ['J-001 step 1'] },
|
|
121
|
+
],
|
|
122
|
+
});
|
|
123
|
+
const prompt = (0, specify_agents_js_1.buildSpecifyAgentPrompt)(agent, ctx);
|
|
124
|
+
assert.ok(prompt.includes('FR-001'));
|
|
125
|
+
assert.ok(prompt.includes('Create task'));
|
|
126
|
+
});
|
|
127
|
+
(0, node_test_1.it)('integrations-checker prompt includes journey and FR context', () => {
|
|
128
|
+
const agent = specify_agents_js_1.SPECIFY_AGENTS.find((a) => a.name === 'integrations-checker');
|
|
129
|
+
const ctx = makeContext({
|
|
130
|
+
journeys: [
|
|
131
|
+
{ id: 'J-001', name: 'Onboarding', type: 'onboarding', preconditions: ['User has account'], steps: [], success: 'Done', failureModes: [], acceptanceTests: [], linkedFRs: [], linkedNFRs: [] },
|
|
132
|
+
],
|
|
133
|
+
frs: [
|
|
134
|
+
{ id: 'FR-001', description: 'Sign up', scope: 'v1', priority: 'P0', sourceJourneys: ['J-001'], sourceSteps: ['J-001 step 1'] },
|
|
135
|
+
],
|
|
136
|
+
});
|
|
137
|
+
const prompt = (0, specify_agents_js_1.buildSpecifyAgentPrompt)(agent, ctx);
|
|
138
|
+
assert.ok(prompt.includes('J-001'));
|
|
139
|
+
assert.ok(prompt.includes('FR-001'));
|
|
140
|
+
});
|
|
141
|
+
(0, node_test_1.it)('integrations-checker prompt includes auto policy', () => {
|
|
142
|
+
const agent = specify_agents_js_1.SPECIFY_AGENTS.find((a) => a.name === 'integrations-checker');
|
|
143
|
+
const ctx = makeContext({ autoPolicy: 'balanced' });
|
|
144
|
+
const prompt = (0, specify_agents_js_1.buildSpecifyAgentPrompt)(agent, ctx);
|
|
145
|
+
assert.ok(prompt.includes('balanced'));
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
// ─── orchestrateSpecifyAgents ───────────────────────────────────────────────
|
|
149
|
+
(0, node_test_1.describe)('orchestrateSpecifyAgents', () => {
|
|
150
|
+
(0, node_test_1.it)('returns results for all agents', async () => {
|
|
151
|
+
const spawnFn = makeSpawnFn({
|
|
152
|
+
'journey-mapper': '### J-001: Onboarding',
|
|
153
|
+
'architecture-drafter': '### Components\n### Data Model',
|
|
154
|
+
'integrations-checker': '## Integrations',
|
|
155
|
+
});
|
|
156
|
+
const results = await (0, specify_agents_js_1.orchestrateSpecifyAgents)(specify_agents_js_1.SPECIFY_AGENTS, makeContext(), spawnFn);
|
|
157
|
+
assert.strictEqual(results.length, 3);
|
|
158
|
+
});
|
|
159
|
+
(0, node_test_1.it)('journey-mapper runs first (sequential phase)', async () => {
|
|
160
|
+
const order = [];
|
|
161
|
+
const spawnFn = async (prompt) => {
|
|
162
|
+
if (prompt.includes('journey-mapper')) {
|
|
163
|
+
order.push('journey-mapper');
|
|
164
|
+
// Small delay to ensure ordering visibility
|
|
165
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
166
|
+
}
|
|
167
|
+
else if (prompt.includes('architecture-drafter')) {
|
|
168
|
+
order.push('architecture-drafter');
|
|
169
|
+
}
|
|
170
|
+
else if (prompt.includes('integrations-checker')) {
|
|
171
|
+
order.push('integrations-checker');
|
|
172
|
+
}
|
|
173
|
+
return 'output';
|
|
174
|
+
};
|
|
175
|
+
await (0, specify_agents_js_1.orchestrateSpecifyAgents)(specify_agents_js_1.SPECIFY_AGENTS, makeContext(), spawnFn);
|
|
176
|
+
// journey-mapper should be first
|
|
177
|
+
assert.strictEqual(order[0], 'journey-mapper');
|
|
178
|
+
});
|
|
179
|
+
(0, node_test_1.it)('parallel agents run after sequential agents complete', async () => {
|
|
180
|
+
const timestamps = {};
|
|
181
|
+
const spawnFn = async (prompt) => {
|
|
182
|
+
if (prompt.includes('journey-mapper')) {
|
|
183
|
+
timestamps['journey-mapper-start'] = Date.now();
|
|
184
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
185
|
+
timestamps['journey-mapper-end'] = Date.now();
|
|
186
|
+
}
|
|
187
|
+
else if (prompt.includes('architecture-drafter')) {
|
|
188
|
+
timestamps['architecture-drafter-start'] = Date.now();
|
|
189
|
+
}
|
|
190
|
+
else if (prompt.includes('integrations-checker')) {
|
|
191
|
+
timestamps['integrations-checker-start'] = Date.now();
|
|
192
|
+
}
|
|
193
|
+
return 'output';
|
|
194
|
+
};
|
|
195
|
+
await (0, specify_agents_js_1.orchestrateSpecifyAgents)(specify_agents_js_1.SPECIFY_AGENTS, makeContext(), spawnFn);
|
|
196
|
+
// Parallel agents should start after journey-mapper ends
|
|
197
|
+
assert.ok(timestamps['architecture-drafter-start'] >= timestamps['journey-mapper-end']);
|
|
198
|
+
assert.ok(timestamps['integrations-checker-start'] >= timestamps['journey-mapper-end']);
|
|
199
|
+
});
|
|
200
|
+
(0, node_test_1.it)('failed journey-mapper still allows parallel agents to run', async () => {
|
|
201
|
+
const spawnFn = makeFailingSpawnFn('journey-mapper');
|
|
202
|
+
const results = await (0, specify_agents_js_1.orchestrateSpecifyAgents)(specify_agents_js_1.SPECIFY_AGENTS, makeContext(), spawnFn);
|
|
203
|
+
const jmResult = results.find((r) => r.agent === 'journey-mapper');
|
|
204
|
+
assert.strictEqual(jmResult.status, 'failed');
|
|
205
|
+
const adResult = results.find((r) => r.agent === 'architecture-drafter');
|
|
206
|
+
assert.strictEqual(adResult.status, 'complete');
|
|
207
|
+
const icResult = results.find((r) => r.agent === 'integrations-checker');
|
|
208
|
+
assert.strictEqual(icResult.status, 'complete');
|
|
209
|
+
});
|
|
210
|
+
(0, node_test_1.it)('all agent results collected regardless of individual failures', async () => {
|
|
211
|
+
const spawnFn = async (prompt) => {
|
|
212
|
+
if (prompt.includes('architecture-drafter')) {
|
|
213
|
+
throw new Error('Architecture agent crashed');
|
|
214
|
+
}
|
|
215
|
+
return 'output';
|
|
216
|
+
};
|
|
217
|
+
const results = await (0, specify_agents_js_1.orchestrateSpecifyAgents)(specify_agents_js_1.SPECIFY_AGENTS, makeContext(), spawnFn);
|
|
218
|
+
assert.strictEqual(results.length, 3);
|
|
219
|
+
const failedAgent = results.find((r) => r.agent === 'architecture-drafter');
|
|
220
|
+
assert.strictEqual(failedAgent.status, 'failed');
|
|
221
|
+
assert.ok(failedAgent.error?.includes('crashed'));
|
|
222
|
+
});
|
|
223
|
+
(0, node_test_1.it)('results include duration_ms', async () => {
|
|
224
|
+
const spawnFn = async () => 'output';
|
|
225
|
+
const results = await (0, specify_agents_js_1.orchestrateSpecifyAgents)(specify_agents_js_1.SPECIFY_AGENTS, makeContext(), spawnFn);
|
|
226
|
+
for (const result of results) {
|
|
227
|
+
assert.ok(typeof result.duration_ms === 'number');
|
|
228
|
+
assert.ok(result.duration_ms >= 0);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
// ─── Integration validation ─────────────────────────────────────────────────
|
|
233
|
+
(0, node_test_1.describe)('validateIntegration', () => {
|
|
234
|
+
(0, node_test_1.it)('valid integration passes', () => {
|
|
235
|
+
const integration = {
|
|
236
|
+
id: 'I-001',
|
|
237
|
+
name: 'SendGrid',
|
|
238
|
+
setupSteps: ['Create account', 'Get API key'],
|
|
239
|
+
authMethod: 'API key',
|
|
240
|
+
costQuota: 'Free tier: 100 emails/day',
|
|
241
|
+
fallback: 'Use SMTP directly',
|
|
242
|
+
status: 'approved',
|
|
243
|
+
};
|
|
244
|
+
const result = (0, specify_agents_js_1.validateIntegration)(integration);
|
|
245
|
+
assert.strictEqual(result.valid, true);
|
|
246
|
+
assert.strictEqual(result.errors.length, 0);
|
|
247
|
+
});
|
|
248
|
+
(0, node_test_1.it)('invalid I-XXX ID format fails', () => {
|
|
249
|
+
const integration = {
|
|
250
|
+
id: 'INVALID',
|
|
251
|
+
name: 'Test',
|
|
252
|
+
setupSteps: [],
|
|
253
|
+
authMethod: 'None',
|
|
254
|
+
costQuota: 'Free',
|
|
255
|
+
fallback: 'N/A',
|
|
256
|
+
status: 'approved',
|
|
257
|
+
};
|
|
258
|
+
const result = (0, specify_agents_js_1.validateIntegration)(integration);
|
|
259
|
+
assert.strictEqual(result.valid, false);
|
|
260
|
+
assert.ok(result.errors.some((e) => e.includes('Invalid integration ID')));
|
|
261
|
+
});
|
|
262
|
+
(0, node_test_1.it)('deferred integration without fallback fails', () => {
|
|
263
|
+
const integration = {
|
|
264
|
+
id: 'I-001',
|
|
265
|
+
name: 'Stripe',
|
|
266
|
+
setupSteps: ['Create account'],
|
|
267
|
+
authMethod: 'API key',
|
|
268
|
+
costQuota: '$25/month',
|
|
269
|
+
fallback: '',
|
|
270
|
+
status: 'deferred with fallback',
|
|
271
|
+
};
|
|
272
|
+
const result = (0, specify_agents_js_1.validateIntegration)(integration);
|
|
273
|
+
assert.strictEqual(result.valid, false);
|
|
274
|
+
assert.ok(result.errors.some((e) => e.includes('no fallback')));
|
|
275
|
+
});
|
|
276
|
+
(0, node_test_1.it)('Status field must be a valid value', () => {
|
|
277
|
+
const integration = {
|
|
278
|
+
id: 'I-001',
|
|
279
|
+
name: 'Test',
|
|
280
|
+
setupSteps: [],
|
|
281
|
+
authMethod: 'None',
|
|
282
|
+
costQuota: 'Free',
|
|
283
|
+
fallback: 'N/A',
|
|
284
|
+
status: 'maybe',
|
|
285
|
+
};
|
|
286
|
+
const result = (0, specify_agents_js_1.validateIntegration)(integration);
|
|
287
|
+
assert.strictEqual(result.valid, false);
|
|
288
|
+
assert.ok(result.errors.some((e) => e.includes('invalid status')));
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
// ─── Component validation ───────────────────────────────────────────────────
|
|
292
|
+
(0, node_test_1.describe)('validateComponent', () => {
|
|
293
|
+
(0, node_test_1.it)('valid component passes', () => {
|
|
294
|
+
const component = {
|
|
295
|
+
id: 'C-001',
|
|
296
|
+
name: 'Auth Module',
|
|
297
|
+
responsibility: 'Handles user authentication and session management',
|
|
298
|
+
dependencies: [],
|
|
299
|
+
linkedFRs: ['FR-001', 'FR-002'],
|
|
300
|
+
};
|
|
301
|
+
const result = (0, specify_agents_js_1.validateComponent)(component);
|
|
302
|
+
assert.strictEqual(result.valid, true);
|
|
303
|
+
assert.strictEqual(result.errors.length, 0);
|
|
304
|
+
});
|
|
305
|
+
(0, node_test_1.it)('invalid C-XXX ID format fails', () => {
|
|
306
|
+
const component = {
|
|
307
|
+
id: 'COMP-1',
|
|
308
|
+
name: 'Test',
|
|
309
|
+
responsibility: 'Does stuff',
|
|
310
|
+
dependencies: [],
|
|
311
|
+
linkedFRs: [],
|
|
312
|
+
};
|
|
313
|
+
const result = (0, specify_agents_js_1.validateComponent)(component);
|
|
314
|
+
assert.strictEqual(result.valid, false);
|
|
315
|
+
assert.ok(result.errors.some((e) => e.includes('Invalid component ID')));
|
|
316
|
+
});
|
|
317
|
+
(0, node_test_1.it)('empty responsibility fails', () => {
|
|
318
|
+
const component = {
|
|
319
|
+
id: 'C-001',
|
|
320
|
+
name: 'Test',
|
|
321
|
+
responsibility: '',
|
|
322
|
+
dependencies: [],
|
|
323
|
+
linkedFRs: [],
|
|
324
|
+
};
|
|
325
|
+
const result = (0, specify_agents_js_1.validateComponent)(component);
|
|
326
|
+
assert.strictEqual(result.valid, false);
|
|
327
|
+
assert.ok(result.errors.some((e) => e.includes('empty responsibility')));
|
|
328
|
+
});
|
|
329
|
+
(0, node_test_1.it)('empty name fails', () => {
|
|
330
|
+
const component = {
|
|
331
|
+
id: 'C-001',
|
|
332
|
+
name: '',
|
|
333
|
+
responsibility: 'Does stuff',
|
|
334
|
+
dependencies: [],
|
|
335
|
+
linkedFRs: [],
|
|
336
|
+
};
|
|
337
|
+
const result = (0, specify_agents_js_1.validateComponent)(component);
|
|
338
|
+
assert.strictEqual(result.valid, false);
|
|
339
|
+
assert.ok(result.errors.some((e) => e.includes('empty name')));
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
// ─── VALID_INTEGRATION_STATUSES ─────────────────────────────────────────────
|
|
343
|
+
(0, node_test_1.describe)('VALID_INTEGRATION_STATUSES', () => {
|
|
344
|
+
(0, node_test_1.it)('contains exactly 3 valid statuses', () => {
|
|
345
|
+
assert.strictEqual(specify_agents_js_1.VALID_INTEGRATION_STATUSES.length, 3);
|
|
346
|
+
});
|
|
347
|
+
(0, node_test_1.it)('includes approved, deferred with fallback, rejected', () => {
|
|
348
|
+
assert.ok(specify_agents_js_1.VALID_INTEGRATION_STATUSES.includes('approved'));
|
|
349
|
+
assert.ok(specify_agents_js_1.VALID_INTEGRATION_STATUSES.includes('deferred with fallback'));
|
|
350
|
+
assert.ok(specify_agents_js_1.VALID_INTEGRATION_STATUSES.includes('rejected'));
|
|
351
|
+
});
|
|
352
|
+
});
|