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,622 @@
1
+ /**
2
+ * Release Manager - Release coordination and management
3
+ *
4
+ * Provides release management capabilities:
5
+ * - Release planning and tracking
6
+ * - Feature flag management
7
+ * - Rollback procedures
8
+ * - Release notes generation
9
+ */
10
+
11
+ const { EventEmitter } = require('events');
12
+
13
+ /**
14
+ * Release States
15
+ */
16
+ const ReleaseState = {
17
+ PLANNING: 'planning',
18
+ DEVELOPMENT: 'development',
19
+ TESTING: 'testing',
20
+ STAGING: 'staging',
21
+ CANARY: 'canary',
22
+ PRODUCTION: 'production',
23
+ ROLLBACK: 'rollback',
24
+ COMPLETED: 'completed',
25
+ CANCELLED: 'cancelled'
26
+ };
27
+
28
+ /**
29
+ * Release Types
30
+ */
31
+ const ReleaseType = {
32
+ MAJOR: 'major',
33
+ MINOR: 'minor',
34
+ PATCH: 'patch',
35
+ HOTFIX: 'hotfix',
36
+ CANARY: 'canary'
37
+ };
38
+
39
+ /**
40
+ * Feature Flag Status
41
+ */
42
+ const FeatureFlagStatus = {
43
+ ENABLED: 'enabled',
44
+ DISABLED: 'disabled',
45
+ PERCENTAGE: 'percentage',
46
+ USER_LIST: 'user-list'
47
+ };
48
+
49
+ /**
50
+ * Release definition
51
+ */
52
+ class Release {
53
+ constructor(options) {
54
+ this.id = options.id || this._generateId();
55
+ this.version = options.version;
56
+ this.type = options.type || ReleaseType.MINOR;
57
+ this.name = options.name || `Release ${options.version}`;
58
+ this.description = options.description || '';
59
+ this.state = options.state || ReleaseState.PLANNING;
60
+ this.targetDate = options.targetDate || null;
61
+ this.createdAt = options.createdAt || new Date();
62
+ this.updatedAt = options.updatedAt || new Date();
63
+ this.completedAt = null;
64
+
65
+ this.features = options.features || [];
66
+ this.bugFixes = options.bugFixes || [];
67
+ this.breakingChanges = options.breakingChanges || [];
68
+ this.dependencies = options.dependencies || [];
69
+
70
+ this.rolloutStrategy = options.rolloutStrategy || {
71
+ type: 'percentage',
72
+ stages: [
73
+ { percentage: 1, duration: '1h' },
74
+ { percentage: 10, duration: '2h' },
75
+ { percentage: 50, duration: '4h' },
76
+ { percentage: 100, duration: null }
77
+ ]
78
+ };
79
+
80
+ this.rollbackPlan = options.rollbackPlan || {
81
+ automatic: true,
82
+ triggers: ['error_rate > 5%', 'latency_p99 > 2s'],
83
+ procedure: []
84
+ };
85
+
86
+ this.metrics = {
87
+ errorsBefore: null,
88
+ errorsAfter: null,
89
+ latencyBefore: null,
90
+ latencyAfter: null
91
+ };
92
+
93
+ this.history = [];
94
+ this._addHistory('created', { version: this.version });
95
+ }
96
+
97
+ /**
98
+ * Transition to a new state
99
+ */
100
+ transitionTo(newState, metadata = {}) {
101
+ const validTransitions = {
102
+ [ReleaseState.PLANNING]: [ReleaseState.DEVELOPMENT, ReleaseState.CANCELLED],
103
+ [ReleaseState.DEVELOPMENT]: [ReleaseState.TESTING, ReleaseState.CANCELLED],
104
+ [ReleaseState.TESTING]: [ReleaseState.STAGING, ReleaseState.DEVELOPMENT, ReleaseState.CANCELLED],
105
+ [ReleaseState.STAGING]: [ReleaseState.CANARY, ReleaseState.PRODUCTION, ReleaseState.TESTING, ReleaseState.CANCELLED],
106
+ [ReleaseState.CANARY]: [ReleaseState.PRODUCTION, ReleaseState.ROLLBACK],
107
+ [ReleaseState.PRODUCTION]: [ReleaseState.COMPLETED, ReleaseState.ROLLBACK],
108
+ [ReleaseState.ROLLBACK]: [ReleaseState.TESTING, ReleaseState.CANCELLED],
109
+ [ReleaseState.COMPLETED]: [],
110
+ [ReleaseState.CANCELLED]: []
111
+ };
112
+
113
+ const allowed = validTransitions[this.state] || [];
114
+ if (!allowed.includes(newState)) {
115
+ throw new Error(`Invalid transition from ${this.state} to ${newState}`);
116
+ }
117
+
118
+ const previousState = this.state;
119
+ this.state = newState;
120
+ this.updatedAt = new Date();
121
+
122
+ if (newState === ReleaseState.COMPLETED) {
123
+ this.completedAt = new Date();
124
+ }
125
+
126
+ this._addHistory('transition', {
127
+ from: previousState,
128
+ to: newState,
129
+ ...metadata
130
+ });
131
+
132
+ return this;
133
+ }
134
+
135
+ /**
136
+ * Add a feature to the release
137
+ */
138
+ addFeature(feature) {
139
+ this.features.push({
140
+ id: feature.id || `feat-${this.features.length + 1}`,
141
+ title: feature.title,
142
+ description: feature.description || '',
143
+ jiraId: feature.jiraId || null,
144
+ breaking: feature.breaking || false
145
+ });
146
+ this._addHistory('featureAdded', { feature: feature.title });
147
+ return this;
148
+ }
149
+
150
+ /**
151
+ * Add a bug fix to the release
152
+ */
153
+ addBugFix(bugFix) {
154
+ this.bugFixes.push({
155
+ id: bugFix.id || `fix-${this.bugFixes.length + 1}`,
156
+ title: bugFix.title,
157
+ description: bugFix.description || '',
158
+ jiraId: bugFix.jiraId || null,
159
+ severity: bugFix.severity || 'medium'
160
+ });
161
+ this._addHistory('bugFixAdded', { bugFix: bugFix.title });
162
+ return this;
163
+ }
164
+
165
+ /**
166
+ * Generate release notes
167
+ */
168
+ generateReleaseNotes(format = 'markdown') {
169
+ const notes = {
170
+ version: this.version,
171
+ date: this.completedAt || new Date(),
172
+ features: this.features,
173
+ bugFixes: this.bugFixes,
174
+ breakingChanges: this.breakingChanges
175
+ };
176
+
177
+ if (format === 'markdown') {
178
+ return this._toMarkdown(notes);
179
+ } else if (format === 'json') {
180
+ return JSON.stringify(notes, null, 2);
181
+ }
182
+ return notes;
183
+ }
184
+
185
+ /**
186
+ * Generate markdown release notes
187
+ * @private
188
+ */
189
+ _toMarkdown(notes) {
190
+ let md = `# ${this.name}\n\n`;
191
+ md += `**Version:** ${notes.version} \n`;
192
+ md += `**Date:** ${notes.date.toISOString().split('T')[0]} \n`;
193
+ md += `**Type:** ${this.type} \n\n`;
194
+
195
+ if (this.description) {
196
+ md += `${this.description}\n\n`;
197
+ }
198
+
199
+ if (notes.breakingChanges.length > 0) {
200
+ md += `## ⚠️ Breaking Changes\n\n`;
201
+ for (const change of notes.breakingChanges) {
202
+ md += `- ${change.title}\n`;
203
+ if (change.description) md += ` ${change.description}\n`;
204
+ }
205
+ md += '\n';
206
+ }
207
+
208
+ if (notes.features.length > 0) {
209
+ md += `## ✨ New Features\n\n`;
210
+ for (const feature of notes.features) {
211
+ md += `- **${feature.title}**`;
212
+ if (feature.jiraId) md += ` (${feature.jiraId})`;
213
+ md += '\n';
214
+ if (feature.description) md += ` ${feature.description}\n`;
215
+ }
216
+ md += '\n';
217
+ }
218
+
219
+ if (notes.bugFixes.length > 0) {
220
+ md += `## 🐛 Bug Fixes\n\n`;
221
+ for (const fix of notes.bugFixes) {
222
+ md += `- ${fix.title}`;
223
+ if (fix.jiraId) md += ` (${fix.jiraId})`;
224
+ md += '\n';
225
+ }
226
+ md += '\n';
227
+ }
228
+
229
+ return md;
230
+ }
231
+
232
+ /**
233
+ * Add history entry
234
+ * @private
235
+ */
236
+ _addHistory(action, data) {
237
+ this.history.push({
238
+ action,
239
+ timestamp: new Date(),
240
+ data
241
+ });
242
+ }
243
+
244
+ /**
245
+ * Generate unique ID
246
+ * @private
247
+ */
248
+ _generateId() {
249
+ return `rel-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
250
+ }
251
+
252
+ toJSON() {
253
+ return {
254
+ id: this.id,
255
+ version: this.version,
256
+ type: this.type,
257
+ name: this.name,
258
+ description: this.description,
259
+ state: this.state,
260
+ targetDate: this.targetDate,
261
+ createdAt: this.createdAt,
262
+ updatedAt: this.updatedAt,
263
+ completedAt: this.completedAt,
264
+ features: this.features,
265
+ bugFixes: this.bugFixes,
266
+ breakingChanges: this.breakingChanges,
267
+ rolloutStrategy: this.rolloutStrategy,
268
+ rollbackPlan: this.rollbackPlan,
269
+ history: this.history
270
+ };
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Feature Flag definition
276
+ */
277
+ class FeatureFlag {
278
+ constructor(options) {
279
+ this.key = options.key;
280
+ this.name = options.name || options.key;
281
+ this.description = options.description || '';
282
+ this.status = options.status || FeatureFlagStatus.DISABLED;
283
+ this.percentage = options.percentage || 0;
284
+ this.userList = options.userList || [];
285
+ this.createdAt = options.createdAt || new Date();
286
+ this.updatedAt = options.updatedAt || new Date();
287
+ this.metadata = options.metadata || {};
288
+ }
289
+
290
+ /**
291
+ * Enable the feature flag
292
+ */
293
+ enable() {
294
+ this.status = FeatureFlagStatus.ENABLED;
295
+ this.percentage = 100;
296
+ this.updatedAt = new Date();
297
+ return this;
298
+ }
299
+
300
+ /**
301
+ * Disable the feature flag
302
+ */
303
+ disable() {
304
+ this.status = FeatureFlagStatus.DISABLED;
305
+ this.percentage = 0;
306
+ this.updatedAt = new Date();
307
+ return this;
308
+ }
309
+
310
+ /**
311
+ * Set percentage rollout
312
+ */
313
+ setPercentage(pct) {
314
+ if (pct < 0 || pct > 100) {
315
+ throw new Error('Percentage must be between 0 and 100');
316
+ }
317
+ this.status = FeatureFlagStatus.PERCENTAGE;
318
+ this.percentage = pct;
319
+ this.updatedAt = new Date();
320
+ return this;
321
+ }
322
+
323
+ /**
324
+ * Check if feature is enabled for a user
325
+ */
326
+ isEnabledFor(userId) {
327
+ switch (this.status) {
328
+ case FeatureFlagStatus.ENABLED:
329
+ return true;
330
+
331
+ case FeatureFlagStatus.DISABLED:
332
+ return false;
333
+
334
+ case FeatureFlagStatus.USER_LIST:
335
+ return this.userList.includes(userId);
336
+
337
+ case FeatureFlagStatus.PERCENTAGE:
338
+ // Consistent hashing based on userId
339
+ const hash = this._hashString(`${this.key}:${userId}`);
340
+ return (hash % 100) < this.percentage;
341
+
342
+ default:
343
+ return false;
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Simple string hash
349
+ * @private
350
+ */
351
+ _hashString(str) {
352
+ let hash = 0;
353
+ for (let i = 0; i < str.length; i++) {
354
+ const char = str.charCodeAt(i);
355
+ hash = ((hash << 5) - hash) + char;
356
+ hash = hash & hash;
357
+ }
358
+ return Math.abs(hash);
359
+ }
360
+
361
+ toJSON() {
362
+ return {
363
+ key: this.key,
364
+ name: this.name,
365
+ description: this.description,
366
+ status: this.status,
367
+ percentage: this.percentage,
368
+ userList: this.userList,
369
+ createdAt: this.createdAt,
370
+ updatedAt: this.updatedAt,
371
+ metadata: this.metadata
372
+ };
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Release Manager
378
+ */
379
+ class ReleaseManager extends EventEmitter {
380
+ constructor(options = {}) {
381
+ super();
382
+ this.releases = new Map();
383
+ this.featureFlags = new Map();
384
+ this.options = {
385
+ autoGenerateNotes: options.autoGenerateNotes !== false,
386
+ ...options
387
+ };
388
+ }
389
+
390
+ /**
391
+ * Create a new release
392
+ */
393
+ createRelease(options) {
394
+ const release = options instanceof Release ? options : new Release(options);
395
+ this.releases.set(release.id, release);
396
+ this.emit('releaseCreated', release);
397
+ return release;
398
+ }
399
+
400
+ /**
401
+ * Get a release by ID
402
+ */
403
+ getRelease(id) {
404
+ return this.releases.get(id);
405
+ }
406
+
407
+ /**
408
+ * Get release by version
409
+ */
410
+ getReleaseByVersion(version) {
411
+ for (const release of this.releases.values()) {
412
+ if (release.version === version) {
413
+ return release;
414
+ }
415
+ }
416
+ return null;
417
+ }
418
+
419
+ /**
420
+ * List all releases
421
+ */
422
+ listReleases(filter = {}) {
423
+ let releases = [...this.releases.values()];
424
+
425
+ if (filter.state) {
426
+ releases = releases.filter(r => r.state === filter.state);
427
+ }
428
+ if (filter.type) {
429
+ releases = releases.filter(r => r.type === filter.type);
430
+ }
431
+
432
+ return releases.map(r => r.toJSON());
433
+ }
434
+
435
+ /**
436
+ * Transition a release to a new state
437
+ */
438
+ transitionRelease(releaseId, newState, metadata = {}) {
439
+ const release = this.releases.get(releaseId);
440
+ if (!release) {
441
+ throw new Error(`Release not found: ${releaseId}`);
442
+ }
443
+
444
+ release.transitionTo(newState, metadata);
445
+ this.emit('releaseTransitioned', { release, newState });
446
+ return release;
447
+ }
448
+
449
+ /**
450
+ * Create a feature flag
451
+ */
452
+ createFeatureFlag(options) {
453
+ const flag = options instanceof FeatureFlag ? options : new FeatureFlag(options);
454
+ this.featureFlags.set(flag.key, flag);
455
+ this.emit('featureFlagCreated', flag);
456
+ return flag;
457
+ }
458
+
459
+ /**
460
+ * Get a feature flag
461
+ */
462
+ getFeatureFlag(key) {
463
+ return this.featureFlags.get(key);
464
+ }
465
+
466
+ /**
467
+ * List all feature flags
468
+ */
469
+ listFeatureFlags() {
470
+ return [...this.featureFlags.values()].map(f => f.toJSON());
471
+ }
472
+
473
+ /**
474
+ * Check if a feature is enabled for a user
475
+ */
476
+ isFeatureEnabled(flagKey, userId = null) {
477
+ const flag = this.featureFlags.get(flagKey);
478
+ if (!flag) return false;
479
+
480
+ if (userId) {
481
+ return flag.isEnabledFor(userId);
482
+ }
483
+ return flag.status === FeatureFlagStatus.ENABLED;
484
+ }
485
+
486
+ /**
487
+ * Enable a feature flag
488
+ */
489
+ enableFeatureFlag(key) {
490
+ const flag = this.featureFlags.get(key);
491
+ if (!flag) throw new Error(`Feature flag not found: ${key}`);
492
+ flag.enable();
493
+ this.emit('featureFlagEnabled', flag);
494
+ return flag;
495
+ }
496
+
497
+ /**
498
+ * Disable a feature flag
499
+ */
500
+ disableFeatureFlag(key) {
501
+ const flag = this.featureFlags.get(key);
502
+ if (!flag) throw new Error(`Feature flag not found: ${key}`);
503
+ flag.disable();
504
+ this.emit('featureFlagDisabled', flag);
505
+ return flag;
506
+ }
507
+
508
+ /**
509
+ * Set feature flag percentage
510
+ */
511
+ setFeatureFlagPercentage(key, percentage) {
512
+ const flag = this.featureFlags.get(key);
513
+ if (!flag) throw new Error(`Feature flag not found: ${key}`);
514
+ flag.setPercentage(percentage);
515
+ this.emit('featureFlagUpdated', flag);
516
+ return flag;
517
+ }
518
+
519
+ /**
520
+ * Generate rollback procedure for a release
521
+ */
522
+ generateRollbackProcedure(releaseId) {
523
+ const release = this.releases.get(releaseId);
524
+ if (!release) throw new Error(`Release not found: ${releaseId}`);
525
+
526
+ return {
527
+ releaseId: release.id,
528
+ version: release.version,
529
+ steps: [
530
+ {
531
+ order: 1,
532
+ action: 'notify',
533
+ description: 'Notify team of rollback initiation',
534
+ command: null
535
+ },
536
+ {
537
+ order: 2,
538
+ action: 'disable-flags',
539
+ description: 'Disable all new feature flags',
540
+ command: 'musubi release disable-flags --version ' + release.version
541
+ },
542
+ {
543
+ order: 3,
544
+ action: 'scale-down',
545
+ description: 'Scale down new deployment',
546
+ command: 'kubectl scale deployment app-v' + release.version + ' --replicas=0'
547
+ },
548
+ {
549
+ order: 4,
550
+ action: 'traffic-shift',
551
+ description: 'Shift traffic to previous version',
552
+ command: 'kubectl rollout undo deployment/app'
553
+ },
554
+ {
555
+ order: 5,
556
+ action: 'verify',
557
+ description: 'Verify rollback success',
558
+ command: 'curl -f http://app/health'
559
+ },
560
+ {
561
+ order: 6,
562
+ action: 'notify-complete',
563
+ description: 'Notify team of rollback completion',
564
+ command: null
565
+ }
566
+ ],
567
+ automaticTriggers: release.rollbackPlan.triggers,
568
+ estimatedDuration: '5-10 minutes'
569
+ };
570
+ }
571
+
572
+ /**
573
+ * Get summary statistics
574
+ */
575
+ getSummary() {
576
+ const releases = [...this.releases.values()];
577
+ const flags = [...this.featureFlags.values()];
578
+
579
+ return {
580
+ totalReleases: releases.length,
581
+ releasesByState: this._countBy(releases, 'state'),
582
+ releasesByType: this._countBy(releases, 'type'),
583
+ totalFeatureFlags: flags.length,
584
+ enabledFlags: flags.filter(f => f.status === FeatureFlagStatus.ENABLED).length,
585
+ disabledFlags: flags.filter(f => f.status === FeatureFlagStatus.DISABLED).length,
586
+ percentageFlags: flags.filter(f => f.status === FeatureFlagStatus.PERCENTAGE).length
587
+ };
588
+ }
589
+
590
+ /**
591
+ * Count items by property
592
+ * @private
593
+ */
594
+ _countBy(items, prop) {
595
+ return items.reduce((acc, item) => {
596
+ acc[item[prop]] = (acc[item[prop]] || 0) + 1;
597
+ return acc;
598
+ }, {});
599
+ }
600
+ }
601
+
602
+ /**
603
+ * Create a release manager
604
+ */
605
+ function createReleaseManager(options = {}) {
606
+ return new ReleaseManager(options);
607
+ }
608
+
609
+ module.exports = {
610
+ // Classes
611
+ Release,
612
+ FeatureFlag,
613
+ ReleaseManager,
614
+
615
+ // Constants
616
+ ReleaseState,
617
+ ReleaseType,
618
+ FeatureFlagStatus,
619
+
620
+ // Factory
621
+ createReleaseManager
622
+ };