musubi-sdd 3.0.1 → 3.5.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.
Files changed (49) hide show
  1. package/bin/musubi-change.js +623 -10
  2. package/bin/musubi-orchestrate.js +456 -0
  3. package/bin/musubi-trace.js +393 -0
  4. package/package.json +3 -2
  5. package/src/analyzers/impact-analyzer.js +682 -0
  6. package/src/integrations/cicd.js +782 -0
  7. package/src/integrations/documentation.js +740 -0
  8. package/src/integrations/examples.js +789 -0
  9. package/src/integrations/index.js +23 -0
  10. package/src/integrations/platforms.js +929 -0
  11. package/src/managers/delta-spec.js +484 -0
  12. package/src/monitoring/incident-manager.js +890 -0
  13. package/src/monitoring/index.js +633 -0
  14. package/src/monitoring/observability.js +938 -0
  15. package/src/monitoring/release-manager.js +622 -0
  16. package/src/orchestration/index.js +168 -0
  17. package/src/orchestration/orchestration-engine.js +409 -0
  18. package/src/orchestration/pattern-registry.js +319 -0
  19. package/src/orchestration/patterns/auto.js +386 -0
  20. package/src/orchestration/patterns/group-chat.js +395 -0
  21. package/src/orchestration/patterns/human-in-loop.js +506 -0
  22. package/src/orchestration/patterns/nested.js +322 -0
  23. package/src/orchestration/patterns/sequential.js +278 -0
  24. package/src/orchestration/patterns/swarm.js +395 -0
  25. package/src/orchestration/workflow-orchestrator.js +738 -0
  26. package/src/reporters/coverage-report.js +452 -0
  27. package/src/reporters/traceability-matrix-report.js +684 -0
  28. package/src/steering/advanced-validation.js +812 -0
  29. package/src/steering/auto-updater.js +670 -0
  30. package/src/steering/index.js +119 -0
  31. package/src/steering/quality-metrics.js +650 -0
  32. package/src/steering/template-constraints.js +789 -0
  33. package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +22 -0
  34. package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +21 -0
  35. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +90 -28
  36. package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +32 -0
  37. package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +27 -0
  38. package/src/templates/agents/claude-code/skills/steering/SKILL.md +30 -0
  39. package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +21 -0
  40. package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +27 -0
  41. package/src/templates/agents/codex/AGENTS.md +36 -1
  42. package/src/templates/agents/cursor/AGENTS.md +36 -1
  43. package/src/templates/agents/gemini-cli/GEMINI.md +36 -1
  44. package/src/templates/agents/github-copilot/AGENTS.md +65 -1
  45. package/src/templates/agents/qwen-code/QWEN.md +36 -1
  46. package/src/templates/agents/windsurf/AGENTS.md +36 -1
  47. package/src/templates/shared/delta-spec-template.md +246 -0
  48. package/src/validators/delta-format.js +474 -0
  49. package/src/validators/traceability-validator.js +561 -0
@@ -0,0 +1,474 @@
1
+ /**
2
+ * Delta Format Validator
3
+ * Validates delta specification format compliance
4
+ *
5
+ * @module validators/delta-format
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { DeltaType } = require('../managers/delta-spec');
11
+
12
+ /**
13
+ * Validation rules for delta specifications
14
+ */
15
+ const ValidationRules = {
16
+ // ID format: PREFIX-IDENTIFIER (e.g., DELTA-AUTH-001, CHG-UI-002)
17
+ ID_PATTERN: /^[A-Z]+-[A-Z0-9]+-\d{3}$/,
18
+
19
+ // Delta marker in markdown: [TYPE] ID: Description
20
+ MARKER_PATTERN: /^\[([A-Z]+)\]\s+([A-Z]+-[A-Z0-9]+-\d{3}):\s+(.+)$/,
21
+
22
+ // Requirement reference: REQ-xxx-nnn
23
+ REQ_PATTERN: /REQ-[A-Z]+-\d{3}/g,
24
+
25
+ // Valid statuses
26
+ VALID_STATUSES: ['proposed', 'approved', 'rejected', 'implemented', 'archived'],
27
+
28
+ // Maximum description length
29
+ MAX_DESCRIPTION_LENGTH: 200,
30
+
31
+ // Required sections in delta markdown
32
+ REQUIRED_SECTIONS: ['Description']
33
+ };
34
+
35
+ class DeltaFormatValidator {
36
+ constructor(options = {}) {
37
+ this.strict = options.strict || false;
38
+ this.errors = [];
39
+ this.warnings = [];
40
+ }
41
+
42
+ /**
43
+ * Validate a delta specification object
44
+ * @param {Object} delta - Delta spec to validate
45
+ * @returns {Object} Validation result
46
+ */
47
+ validate(delta) {
48
+ this.errors = [];
49
+ this.warnings = [];
50
+
51
+ this.validateId(delta.id);
52
+ this.validateType(delta.type);
53
+ this.validateTarget(delta.target);
54
+ this.validateDescription(delta.description);
55
+ this.validateStatus(delta.status);
56
+ this.validateStateTransitions(delta);
57
+ this.validateImpactedAreas(delta.impactedAreas);
58
+ this.validateTimestamps(delta);
59
+
60
+ return {
61
+ valid: this.errors.length === 0,
62
+ errors: this.errors,
63
+ warnings: this.warnings,
64
+ delta
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Validate delta ID format
70
+ * @param {string} id - Delta ID
71
+ */
72
+ validateId(id) {
73
+ if (!id) {
74
+ this.errors.push({
75
+ rule: 'required-id',
76
+ message: 'Delta ID is required'
77
+ });
78
+ return;
79
+ }
80
+
81
+ if (!ValidationRules.ID_PATTERN.test(id)) {
82
+ this.errors.push({
83
+ rule: 'id-format',
84
+ message: `Invalid ID format: "${id}". Expected: PREFIX-IDENTIFIER-NNN (e.g., DELTA-AUTH-001)`,
85
+ value: id
86
+ });
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Validate delta type
92
+ * @param {string} type - Delta type
93
+ */
94
+ validateType(type) {
95
+ if (!type) {
96
+ this.errors.push({
97
+ rule: 'required-type',
98
+ message: 'Delta type is required'
99
+ });
100
+ return;
101
+ }
102
+
103
+ if (!Object.values(DeltaType).includes(type)) {
104
+ this.errors.push({
105
+ rule: 'invalid-type',
106
+ message: `Invalid delta type: "${type}". Must be one of: ${Object.values(DeltaType).join(', ')}`,
107
+ value: type
108
+ });
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Validate target reference
114
+ * @param {string} target - Target requirement/component
115
+ */
116
+ validateTarget(target) {
117
+ if (!target) {
118
+ this.errors.push({
119
+ rule: 'required-target',
120
+ message: 'Target is required'
121
+ });
122
+ return;
123
+ }
124
+
125
+ // Check if target looks like a requirement ID
126
+ if (!ValidationRules.REQ_PATTERN.test(target) && !target.includes('/')) {
127
+ this.warnings.push({
128
+ rule: 'target-format',
129
+ message: `Target "${target}" doesn't match requirement ID format (REQ-XXX-NNN) or path format`,
130
+ value: target
131
+ });
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Validate description
137
+ * @param {string} description - Delta description
138
+ */
139
+ validateDescription(description) {
140
+ if (!description) {
141
+ this.errors.push({
142
+ rule: 'required-description',
143
+ message: 'Description is required'
144
+ });
145
+ return;
146
+ }
147
+
148
+ if (description.length > ValidationRules.MAX_DESCRIPTION_LENGTH) {
149
+ this.warnings.push({
150
+ rule: 'description-length',
151
+ message: `Description exceeds ${ValidationRules.MAX_DESCRIPTION_LENGTH} characters`,
152
+ value: description.length
153
+ });
154
+ }
155
+
156
+ // Check for vague language
157
+ const vagueTerms = ['should', 'might', 'could', 'maybe', 'possibly'];
158
+ const foundVague = vagueTerms.filter(term =>
159
+ description.toLowerCase().includes(term)
160
+ );
161
+
162
+ if (foundVague.length > 0) {
163
+ this.warnings.push({
164
+ rule: 'vague-language',
165
+ message: `Description contains vague terms: ${foundVague.join(', ')}. Use definitive language.`,
166
+ value: foundVague
167
+ });
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Validate status
173
+ * @param {string} status - Delta status
174
+ */
175
+ validateStatus(status) {
176
+ if (!status) {
177
+ this.warnings.push({
178
+ rule: 'missing-status',
179
+ message: 'Status not specified, defaulting to "proposed"'
180
+ });
181
+ return;
182
+ }
183
+
184
+ if (!ValidationRules.VALID_STATUSES.includes(status)) {
185
+ this.errors.push({
186
+ rule: 'invalid-status',
187
+ message: `Invalid status: "${status}". Must be one of: ${ValidationRules.VALID_STATUSES.join(', ')}`,
188
+ value: status
189
+ });
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Validate state transitions based on delta type
195
+ * @param {Object} delta - Delta spec
196
+ */
197
+ validateStateTransitions(delta) {
198
+ const { type, before, after } = delta;
199
+
200
+ switch (type) {
201
+ case DeltaType.ADDED:
202
+ if (before) {
203
+ this.warnings.push({
204
+ rule: 'added-has-before',
205
+ message: 'ADDED delta should not have a "before" state'
206
+ });
207
+ }
208
+ if (!after && this.strict) {
209
+ this.errors.push({
210
+ rule: 'added-missing-after',
211
+ message: 'ADDED delta should specify the new state in "after"'
212
+ });
213
+ }
214
+ break;
215
+
216
+ case DeltaType.MODIFIED:
217
+ if (!before) {
218
+ this.warnings.push({
219
+ rule: 'modified-missing-before',
220
+ message: 'MODIFIED delta should have a "before" state for comparison'
221
+ });
222
+ }
223
+ if (!after) {
224
+ this.warnings.push({
225
+ rule: 'modified-missing-after',
226
+ message: 'MODIFIED delta should have an "after" state'
227
+ });
228
+ }
229
+ break;
230
+
231
+ case DeltaType.REMOVED:
232
+ if (!before && this.strict) {
233
+ this.warnings.push({
234
+ rule: 'removed-missing-before',
235
+ message: 'REMOVED delta should document the removed state in "before"'
236
+ });
237
+ }
238
+ if (after) {
239
+ this.warnings.push({
240
+ rule: 'removed-has-after',
241
+ message: 'REMOVED delta should not have an "after" state'
242
+ });
243
+ }
244
+ break;
245
+
246
+ case DeltaType.RENAMED:
247
+ if (!before) {
248
+ this.errors.push({
249
+ rule: 'renamed-missing-before',
250
+ message: 'RENAMED delta requires "before" state (original name)'
251
+ });
252
+ }
253
+ if (!after) {
254
+ this.errors.push({
255
+ rule: 'renamed-missing-after',
256
+ message: 'RENAMED delta requires "after" state (new name)'
257
+ });
258
+ }
259
+ break;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Validate impacted areas
265
+ * @param {string[]} impactedAreas - List of impacted areas
266
+ */
267
+ validateImpactedAreas(impactedAreas) {
268
+ if (!impactedAreas || impactedAreas.length === 0) {
269
+ this.warnings.push({
270
+ rule: 'missing-impact',
271
+ message: 'Impacted areas should be specified for change tracking'
272
+ });
273
+ return;
274
+ }
275
+
276
+ // Known impact categories
277
+ const knownCategories = [
278
+ 'api', 'database', 'ui', 'backend', 'frontend',
279
+ 'security', 'performance', 'testing', 'documentation',
280
+ 'infrastructure', 'configuration'
281
+ ];
282
+
283
+ const unknown = impactedAreas.filter(area =>
284
+ !knownCategories.includes(area.toLowerCase())
285
+ );
286
+
287
+ if (unknown.length > 0 && this.strict) {
288
+ this.warnings.push({
289
+ rule: 'unknown-impact-area',
290
+ message: `Unknown impact areas: ${unknown.join(', ')}. Consider using: ${knownCategories.join(', ')}`,
291
+ value: unknown
292
+ });
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Validate timestamps
298
+ * @param {Object} delta - Delta spec
299
+ */
300
+ validateTimestamps(delta) {
301
+ if (delta.createdAt) {
302
+ if (isNaN(Date.parse(delta.createdAt))) {
303
+ this.errors.push({
304
+ rule: 'invalid-created-timestamp',
305
+ message: 'Invalid createdAt timestamp format',
306
+ value: delta.createdAt
307
+ });
308
+ }
309
+ }
310
+
311
+ if (delta.updatedAt) {
312
+ if (isNaN(Date.parse(delta.updatedAt))) {
313
+ this.errors.push({
314
+ rule: 'invalid-updated-timestamp',
315
+ message: 'Invalid updatedAt timestamp format',
316
+ value: delta.updatedAt
317
+ });
318
+ }
319
+
320
+ if (delta.createdAt && new Date(delta.updatedAt) < new Date(delta.createdAt)) {
321
+ this.warnings.push({
322
+ rule: 'timestamp-order',
323
+ message: 'updatedAt is before createdAt'
324
+ });
325
+ }
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Validate markdown content for delta markers
331
+ * @param {string} content - Markdown content
332
+ * @returns {Object} Validation result
333
+ */
334
+ validateMarkdown(content) {
335
+ this.errors = [];
336
+ this.warnings = [];
337
+
338
+ const lines = content.split('\n');
339
+ const deltas = [];
340
+
341
+ lines.forEach((line, index) => {
342
+ const match = line.match(ValidationRules.MARKER_PATTERN);
343
+ if (match) {
344
+ const [, type, id, description] = match;
345
+
346
+ const delta = {
347
+ type,
348
+ id,
349
+ description,
350
+ line: index + 1
351
+ };
352
+
353
+ // Validate each found delta marker
354
+ this.validateType(type);
355
+ this.validateId(id);
356
+ this.validateDescription(description);
357
+
358
+ if (this.errors.length === 0) {
359
+ deltas.push(delta);
360
+ }
361
+ }
362
+ });
363
+
364
+ // Check for duplicate IDs
365
+ const ids = deltas.map(d => d.id);
366
+ const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index);
367
+
368
+ if (duplicates.length > 0) {
369
+ this.errors.push({
370
+ rule: 'duplicate-id',
371
+ message: `Duplicate delta IDs found: ${[...new Set(duplicates)].join(', ')}`,
372
+ value: duplicates
373
+ });
374
+ }
375
+
376
+ return {
377
+ valid: this.errors.length === 0,
378
+ errors: this.errors,
379
+ warnings: this.warnings,
380
+ deltas
381
+ };
382
+ }
383
+
384
+ /**
385
+ * Validate a delta file
386
+ * @param {string} filePath - Path to delta file
387
+ * @returns {Object} Validation result
388
+ */
389
+ validateFile(filePath) {
390
+ if (!fs.existsSync(filePath)) {
391
+ return {
392
+ valid: false,
393
+ errors: [{ rule: 'file-not-found', message: `File not found: ${filePath}` }],
394
+ warnings: []
395
+ };
396
+ }
397
+
398
+ const ext = path.extname(filePath);
399
+ const content = fs.readFileSync(filePath, 'utf-8');
400
+
401
+ if (ext === '.json') {
402
+ try {
403
+ const delta = JSON.parse(content);
404
+ return this.validate(delta);
405
+ } catch (e) {
406
+ return {
407
+ valid: false,
408
+ errors: [{ rule: 'invalid-json', message: `Invalid JSON: ${e.message}` }],
409
+ warnings: []
410
+ };
411
+ }
412
+ } else if (ext === '.md') {
413
+ return this.validateMarkdown(content);
414
+ } else {
415
+ return {
416
+ valid: false,
417
+ errors: [{ rule: 'unsupported-format', message: `Unsupported file format: ${ext}` }],
418
+ warnings: []
419
+ };
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Validate all deltas in a directory
425
+ * @param {string} dirPath - Path to changes directory
426
+ * @returns {Object} Validation results
427
+ */
428
+ validateDirectory(dirPath) {
429
+ if (!fs.existsSync(dirPath)) {
430
+ return {
431
+ valid: true,
432
+ results: [],
433
+ summary: { total: 0, valid: 0, invalid: 0 }
434
+ };
435
+ }
436
+
437
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
438
+ const results = [];
439
+ let validCount = 0;
440
+ let invalidCount = 0;
441
+
442
+ for (const entry of entries) {
443
+ if (entry.isDirectory()) {
444
+ const jsonPath = path.join(dirPath, entry.name, 'delta.json');
445
+ if (fs.existsSync(jsonPath)) {
446
+ const result = this.validateFile(jsonPath);
447
+ result.id = entry.name;
448
+ results.push(result);
449
+
450
+ if (result.valid) {
451
+ validCount++;
452
+ } else {
453
+ invalidCount++;
454
+ }
455
+ }
456
+ }
457
+ }
458
+
459
+ return {
460
+ valid: invalidCount === 0,
461
+ results,
462
+ summary: {
463
+ total: results.length,
464
+ valid: validCount,
465
+ invalid: invalidCount
466
+ }
467
+ };
468
+ }
469
+ }
470
+
471
+ module.exports = {
472
+ DeltaFormatValidator,
473
+ ValidationRules
474
+ };