@evolith/smart-cli 0.0.1-beta → 0.0.2-beta

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 (84) hide show
  1. package/ARCHITECTURE.md +667 -89
  2. package/dist/app.module.js +2 -0
  3. package/dist/app.module.js.map +1 -1
  4. package/dist/application/services/index.d.ts +14 -5
  5. package/dist/application/services/index.js +41 -3
  6. package/dist/application/services/index.js.map +1 -1
  7. package/dist/application/services/phase-transition.use-case.spec.d.ts +1 -0
  8. package/dist/application/services/phase-transition.use-case.spec.js +297 -0
  9. package/dist/application/services/phase-transition.use-case.spec.js.map +1 -0
  10. package/dist/commands/docs/docs.command.d.ts +5 -0
  11. package/dist/commands/docs/docs.command.js +159 -3
  12. package/dist/commands/docs/docs.command.js.map +1 -1
  13. package/dist/commands/drift/drift.command.d.ts +24 -0
  14. package/dist/commands/drift/drift.command.js +250 -0
  15. package/dist/commands/drift/drift.command.js.map +1 -0
  16. package/dist/commands/init/agents.command.spec.d.ts +1 -0
  17. package/dist/commands/init/agents.command.spec.js +152 -0
  18. package/dist/commands/init/agents.command.spec.js.map +1 -0
  19. package/dist/commands/init/upgrade.command.d.ts +10 -0
  20. package/dist/commands/init/upgrade.command.js +134 -1
  21. package/dist/commands/init/upgrade.command.js.map +1 -1
  22. package/dist/commands/sdlc/gate-status.command.d.ts +5 -0
  23. package/dist/commands/sdlc/gate-status.command.js +114 -0
  24. package/dist/commands/sdlc/gate-status.command.js.map +1 -0
  25. package/dist/commands/sdlc/sdlc.command.js +3 -1
  26. package/dist/commands/sdlc/sdlc.command.js.map +1 -1
  27. package/dist/commands/validate/validate.command.d.ts +4 -0
  28. package/dist/commands/validate/validate.command.js +39 -0
  29. package/dist/commands/validate/validate.command.js.map +1 -1
  30. package/dist/core/agents/agent-ruleset-builder.d.ts +30 -0
  31. package/dist/core/agents/agent-ruleset-builder.js +75 -0
  32. package/dist/core/agents/agent-ruleset-builder.js.map +1 -0
  33. package/dist/core/agents/agent-ruleset-builder.spec.d.ts +1 -0
  34. package/dist/core/agents/agent-ruleset-builder.spec.js +135 -0
  35. package/dist/core/agents/agent-ruleset-builder.spec.js.map +1 -0
  36. package/dist/core/mcp/server.d.ts +14 -9
  37. package/dist/core/mcp/server.js +236 -181
  38. package/dist/core/mcp/server.js.map +1 -1
  39. package/dist/core/services/command-history.service.spec.d.ts +1 -0
  40. package/dist/core/services/command-history.service.spec.js +166 -0
  41. package/dist/core/services/command-history.service.spec.js.map +1 -0
  42. package/dist/core/upgrade/satellite-upgrade.service.d.ts +48 -0
  43. package/dist/core/upgrade/satellite-upgrade.service.js +358 -0
  44. package/dist/core/upgrade/satellite-upgrade.service.js.map +1 -0
  45. package/dist/core/upgrade/satellite-upgrade.service.spec.d.ts +1 -0
  46. package/dist/core/upgrade/satellite-upgrade.service.spec.js +143 -0
  47. package/dist/core/upgrade/satellite-upgrade.service.spec.js.map +1 -0
  48. package/dist/core/validators/architecture-drift.service.d.ts +68 -0
  49. package/dist/core/validators/architecture-drift.service.js +266 -0
  50. package/dist/core/validators/architecture-drift.service.js.map +1 -0
  51. package/dist/core/validators/architecture-drift.service.spec.d.ts +1 -0
  52. package/dist/core/validators/architecture-drift.service.spec.js +315 -0
  53. package/dist/core/validators/architecture-drift.service.spec.js.map +1 -0
  54. package/dist/core/validators/phase-gate-validator.service.d.ts +71 -0
  55. package/dist/core/validators/phase-gate-validator.service.js +271 -0
  56. package/dist/core/validators/phase-gate-validator.service.js.map +1 -0
  57. package/dist/core/validators/phase-gate-validator.service.spec.d.ts +1 -0
  58. package/dist/core/validators/phase-gate-validator.service.spec.js +326 -0
  59. package/dist/core/validators/phase-gate-validator.service.spec.js.map +1 -0
  60. package/dist/core/validators/ruleset-validator-architecture.spec.d.ts +1 -0
  61. package/dist/core/validators/ruleset-validator-architecture.spec.js +178 -0
  62. package/dist/core/validators/ruleset-validator-architecture.spec.js.map +1 -0
  63. package/dist/core/validators/ruleset-validator.service.d.ts +24 -0
  64. package/dist/core/validators/ruleset-validator.service.js +394 -0
  65. package/dist/core/validators/ruleset-validator.service.js.map +1 -1
  66. package/dist/core/validators/ruleset-validator.service.spec.js +4 -3
  67. package/dist/core/validators/ruleset-validator.service.spec.js.map +1 -1
  68. package/dist/domain/services/adr.service.spec.d.ts +1 -0
  69. package/dist/domain/services/adr.service.spec.js +141 -0
  70. package/dist/domain/services/adr.service.spec.js.map +1 -0
  71. package/dist/domain/services/agent-registry.service.spec.d.ts +1 -0
  72. package/dist/domain/services/agent-registry.service.spec.js +162 -0
  73. package/dist/domain/services/agent-registry.service.spec.js.map +1 -0
  74. package/dist/domain/services/standards.service.spec.d.ts +1 -0
  75. package/dist/domain/services/standards.service.spec.js +140 -0
  76. package/dist/domain/services/standards.service.spec.js.map +1 -0
  77. package/dist/domain/services/tool-usage-telemetry.service.spec.d.ts +1 -0
  78. package/dist/domain/services/tool-usage-telemetry.service.spec.js +180 -0
  79. package/dist/domain/services/tool-usage-telemetry.service.spec.js.map +1 -0
  80. package/dist/infrastructure/formatters/output-formatter.service.spec.d.ts +1 -0
  81. package/dist/infrastructure/formatters/output-formatter.service.spec.js +164 -0
  82. package/dist/infrastructure/formatters/output-formatter.service.spec.js.map +1 -0
  83. package/dist/main.js +0 -0
  84. package/package.json +1 -1
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ruleset_validator_service_1 = require("./ruleset-validator.service");
4
+ const abstractions_1 = require("../abstractions");
5
+ const createMockFileSystem = (overrides) => {
6
+ const mock = {
7
+ exists: jest.fn().mockResolvedValue(true),
8
+ existsSync: jest.fn().mockReturnValue(true),
9
+ readFile: jest.fn().mockResolvedValue(''),
10
+ readJson: jest.fn().mockResolvedValue({}),
11
+ readdirNames: jest.fn().mockResolvedValue([]),
12
+ writeFile: jest.fn().mockResolvedValue(undefined),
13
+ writeJson: jest.fn().mockResolvedValue(undefined),
14
+ ensureDir: jest.fn().mockResolvedValue(undefined),
15
+ remove: jest.fn().mockResolvedValue(undefined),
16
+ stat: jest.fn().mockResolvedValue({ isDirectory: () => true, isFile: () => false }),
17
+ ...overrides,
18
+ };
19
+ return mock;
20
+ };
21
+ const mockF1Ruleset = {
22
+ rules: [
23
+ {
24
+ id: 'F1-R01',
25
+ severity: 'MUST',
26
+ category: 'topology',
27
+ title: 'Single Deployment Unit',
28
+ description: 'All bounded contexts MUST be deployed as a single artifact',
29
+ blocking: true,
30
+ },
31
+ {
32
+ id: 'F1-R02',
33
+ severity: 'MUST',
34
+ category: 'bounded-contexts',
35
+ title: 'Explicit Bounded Context Boundaries',
36
+ description: 'Each bounded context MUST be clearly separated',
37
+ blocking: true,
38
+ },
39
+ {
40
+ id: 'F1-R03',
41
+ severity: 'MUST',
42
+ category: 'hexagonal-architecture',
43
+ title: 'Ports and Adapters Boundary',
44
+ description: 'All bounded contexts MUST implement hexagonal architecture',
45
+ blocking: true,
46
+ },
47
+ ],
48
+ };
49
+ describe('RulesetValidatorService - Architecture Validation', () => {
50
+ let service;
51
+ let mockFs;
52
+ beforeEach(() => {
53
+ (0, abstractions_1.resetContainer)();
54
+ jest.clearAllMocks();
55
+ mockFs = createMockFileSystem({
56
+ readFile: jest.fn().mockImplementation((p) => {
57
+ if (p.includes('f1-modular-monolith'))
58
+ return Promise.resolve(JSON.stringify(mockF1Ruleset));
59
+ if (p.includes('f2-distributed-modules'))
60
+ return Promise.resolve(JSON.stringify({ rules: [] }));
61
+ if (p.includes('f3-microservices'))
62
+ return Promise.resolve(JSON.stringify({ rules: [] }));
63
+ return Promise.resolve('');
64
+ }),
65
+ });
66
+ const mockProvider = {
67
+ createFileSystem: () => mockFs,
68
+ };
69
+ (0, abstractions_1.getContainer)().setFileSystemProvider(mockProvider);
70
+ service = new ruleset_validator_service_1.RulesetValidatorService();
71
+ });
72
+ afterEach(() => {
73
+ (0, abstractions_1.resetContainer)();
74
+ });
75
+ describe('validateArchitecture', () => {
76
+ it('should validate F1 architecture level', async () => {
77
+ const result = await service.validateArchitecture('/project', '/core', 'F1');
78
+ expect(result).toHaveProperty('status');
79
+ expect(result).toHaveProperty('levels');
80
+ expect(result).toHaveProperty('rulesChecked');
81
+ expect(result).toHaveProperty('issues');
82
+ expect(result).toHaveProperty('timestamp');
83
+ expect(result.levels).toContain('F1');
84
+ });
85
+ it('should validate ALL architecture levels by default', async () => {
86
+ const result = await service.validateArchitecture('/project', '/core');
87
+ expect(result.levels).toContain('F1');
88
+ expect(result.levels).toContain('F2');
89
+ expect(result.levels).toContain('F3');
90
+ });
91
+ it('should validate specific level when specified', async () => {
92
+ const result = await service.validateArchitecture('/project', '/core', 'F2');
93
+ expect(result.levels).toEqual(['F2']);
94
+ });
95
+ it('should return passed status when no blocking issues', async () => {
96
+ const result = await service.validateArchitecture('/project', '/core', 'F1');
97
+ expect(result.status).toBe('passed');
98
+ });
99
+ it('should return issues array even when validation passes', async () => {
100
+ const result = await service.validateArchitecture('/project', '/core', 'F1');
101
+ expect(Array.isArray(result.issues)).toBe(true);
102
+ });
103
+ it('should count rules checked', async () => {
104
+ const result = await service.validateArchitecture('/project', '/core', 'F1');
105
+ expect(result.rulesChecked).toBeGreaterThan(0);
106
+ });
107
+ it('should handle missing ruleset gracefully', async () => {
108
+ mockFs.exists.mockResolvedValue(false);
109
+ const result = await service.validateArchitecture('/project', '/core', 'F1');
110
+ expect(result.issues.some((i) => i.ruleId.includes('MISSING'))).toBe(true);
111
+ });
112
+ });
113
+ describe('validateArchitectureRule - topology', () => {
114
+ it('should validate topology rules for F1', async () => {
115
+ mockFs.readFile.mockImplementation((p) => {
116
+ if (p.includes('f1-modular-monolith')) {
117
+ return Promise.resolve(JSON.stringify({
118
+ rules: [{
119
+ id: 'F1-R01',
120
+ severity: 'MUST',
121
+ category: 'topology',
122
+ title: 'Single Deployment Unit',
123
+ description: 'All bounded contexts MUST be deployed as a single artifact',
124
+ blocking: true,
125
+ }],
126
+ }));
127
+ }
128
+ return Promise.resolve(JSON.stringify({ rules: [] }));
129
+ });
130
+ const result = await service.validateArchitecture('/project', '/core', 'F1');
131
+ expect(result.rulesChecked).toBeGreaterThan(0);
132
+ });
133
+ });
134
+ describe('validateArchitectureRule - bounded-contexts', () => {
135
+ it('should validate bounded contexts rules', async () => {
136
+ mockFs.readFile.mockImplementation((p) => {
137
+ if (p.includes('f1-modular-monolith')) {
138
+ return Promise.resolve(JSON.stringify({
139
+ rules: [{
140
+ id: 'F1-R02',
141
+ severity: 'MUST',
142
+ category: 'bounded-contexts',
143
+ title: 'Explicit Bounded Context Boundaries',
144
+ description: 'Each bounded context MUST be clearly separated',
145
+ blocking: true,
146
+ }],
147
+ }));
148
+ }
149
+ return Promise.resolve(JSON.stringify({ rules: [] }));
150
+ });
151
+ mockFs.readdirNames.mockResolvedValue(['context-a', 'context-b']);
152
+ const result = await service.validateArchitecture('/project', '/core', 'F1');
153
+ expect(result.rulesChecked).toBeGreaterThan(0);
154
+ });
155
+ });
156
+ describe('validateArchitectureRule - hexagonal-architecture', () => {
157
+ it('should validate hexagonal architecture rules', async () => {
158
+ mockFs.readFile.mockImplementation((p) => {
159
+ if (p.includes('f1-modular-monolith')) {
160
+ return Promise.resolve(JSON.stringify({
161
+ rules: [{
162
+ id: 'F1-R03',
163
+ severity: 'MUST',
164
+ category: 'hexagonal-architecture',
165
+ title: 'Ports and Adapters Boundary',
166
+ description: 'All bounded contexts MUST implement hexagonal architecture',
167
+ blocking: true,
168
+ }],
169
+ }));
170
+ }
171
+ return Promise.resolve(JSON.stringify({ rules: [] }));
172
+ });
173
+ const result = await service.validateArchitecture('/project', '/core', 'F1');
174
+ expect(result.rulesChecked).toBeGreaterThan(0);
175
+ });
176
+ });
177
+ });
178
+ //# sourceMappingURL=ruleset-validator-architecture.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ruleset-validator-architecture.spec.js","sourceRoot":"","sources":["../../../src/core/validators/ruleset-validator-architecture.spec.ts"],"names":[],"mappings":"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"}
@@ -53,4 +53,28 @@ export declare class RulesetValidatorService {
53
53
  private findCorePath;
54
54
  private isValidSemver;
55
55
  loadRulesetById(corePath: string, rulesetId: string): Promise<ValidationIssue[]>;
56
+ validateArchitecture(satellitePath: string, corePath?: string, level?: 'F1' | 'F2' | 'F3' | 'ALL'): Promise<ArchitectureValidationResult>;
57
+ private validateArchitectureRule;
58
+ private validateTopologyRule;
59
+ private validateBoundedContextsRule;
60
+ private validateHexagonalRule;
61
+ private validateCommunicationRule;
62
+ private validatePersistenceRule;
63
+ private validateAsyncRule;
64
+ private validateExtractionReadinessRule;
65
+ private validateObservabilityRule;
66
+ private validateModuleAutonomyRule;
67
+ private validateContractStabilityRule;
68
+ private validateDataOwnershipRule;
69
+ private validateAsyncCommunicationRule;
70
+ private validateDistributedTracingRule;
71
+ private validateContainerizationRule;
72
+ private validateServiceBoundariesRule;
73
+ }
74
+ export interface ArchitectureValidationResult {
75
+ status: 'passed' | 'failed' | 'warning';
76
+ levels: string[];
77
+ rulesChecked: number;
78
+ issues: ValidationIssue[];
79
+ timestamp: string;
56
80
  }
@@ -258,6 +258,400 @@ class RulesetValidatorService {
258
258
  }
259
259
  return issues;
260
260
  }
261
+ async validateArchitecture(satellitePath, corePath, level) {
262
+ const resolvedCorePath = corePath || this.findCorePath(satellitePath);
263
+ const issues = [];
264
+ let rulesChecked = 0;
265
+ const levels = level === 'ALL' || !level
266
+ ? ['F1', 'F2', 'F3']
267
+ : [level];
268
+ for (const lvl of levels) {
269
+ const rulesetPath = path.join(resolvedCorePath, 'rulesets', 'architecture', `f${lvl.toLowerCase()}-${lvl === 'F1' ? 'modular-monolith' : lvl === 'F2' ? 'distributed-modules' : 'microservices'}.rules.json`);
270
+ if (!await this.fs.exists(rulesetPath)) {
271
+ issues.push({
272
+ ruleId: `ARCH-${lvl}-MISSING`,
273
+ severity: 'SHOULD',
274
+ category: 'architecture',
275
+ title: `${lvl} ruleset not found`,
276
+ description: `Could not find ${lvl} architecture rules at ${rulesetPath}`,
277
+ blocking: false,
278
+ });
279
+ continue;
280
+ }
281
+ const content = await this.fs.readFile(rulesetPath);
282
+ const ruleset = JSON.parse(content);
283
+ const rules = ruleset.rules || [];
284
+ rulesChecked += rules.length;
285
+ for (const rule of rules) {
286
+ const validationResult = await this.validateArchitectureRule(satellitePath, rule, lvl);
287
+ if (validationResult) {
288
+ issues.push(...validationResult);
289
+ }
290
+ }
291
+ }
292
+ const blockingCount = issues.filter(i => i.blocking).length;
293
+ return {
294
+ status: blockingCount > 0 ? 'failed' : 'passed',
295
+ levels: levels,
296
+ rulesChecked,
297
+ issues,
298
+ timestamp: new Date().toISOString(),
299
+ };
300
+ }
301
+ async validateArchitectureRule(satellitePath, rule, level) {
302
+ const issues = [];
303
+ switch (rule.category) {
304
+ case 'topology':
305
+ this.validateTopologyRule(satellitePath, rule, issues);
306
+ break;
307
+ case 'bounded-contexts':
308
+ this.validateBoundedContextsRule(satellitePath, rule, issues);
309
+ break;
310
+ case 'hexagonal-architecture':
311
+ await this.validateHexagonalRule(satellitePath, rule, issues);
312
+ break;
313
+ case 'communication':
314
+ await this.validateCommunicationRule(satellitePath, rule, issues);
315
+ break;
316
+ case 'persistence':
317
+ this.validatePersistenceRule(satellitePath, rule, issues);
318
+ break;
319
+ case 'async-boundaries':
320
+ await this.validateAsyncRule(satellitePath, rule, issues);
321
+ break;
322
+ case 'extraction-readiness':
323
+ this.validateExtractionReadinessRule(satellitePath, rule, issues);
324
+ break;
325
+ case 'observability':
326
+ this.validateObservabilityRule(satellitePath, rule, issues);
327
+ break;
328
+ case 'module-autonomy':
329
+ await this.validateModuleAutonomyRule(satellitePath, rule, issues);
330
+ break;
331
+ case 'contract-stability':
332
+ await this.validateContractStabilityRule(satellitePath, rule, issues);
333
+ break;
334
+ case 'data-ownership':
335
+ this.validateDataOwnershipRule(satellitePath, rule, issues);
336
+ break;
337
+ case 'async-communication':
338
+ await this.validateAsyncCommunicationRule(satellitePath, rule, issues);
339
+ break;
340
+ case 'distributed-tracing':
341
+ this.validateDistributedTracingRule(satellitePath, rule, issues);
342
+ break;
343
+ case 'containerization':
344
+ this.validateContainerizationRule(satellitePath, rule, issues);
345
+ break;
346
+ case 'service-boundaries':
347
+ await this.validateServiceBoundariesRule(satellitePath, rule, issues);
348
+ break;
349
+ default:
350
+ break;
351
+ }
352
+ return issues.length > 0 ? issues : null;
353
+ }
354
+ async validateTopologyRule(satellitePath, rule, issues) {
355
+ const packageJsonPath = path.join(satellitePath, 'package.json');
356
+ if (rule.id === 'F1-R01') {
357
+ if (await this.fs.exists(packageJsonPath)) {
358
+ const pkg = await this.fs.readJson(packageJsonPath);
359
+ if (pkg.workspaces) {
360
+ issues.push({
361
+ ruleId: rule.id,
362
+ severity: rule.severity,
363
+ category: rule.category,
364
+ title: rule.title,
365
+ description: `${rule.description} - Monorepo workspace detected`,
366
+ blocking: rule.blocking,
367
+ });
368
+ }
369
+ }
370
+ }
371
+ }
372
+ async validateBoundedContextsRule(satellitePath, rule, issues) {
373
+ const srcPath = path.join(satellitePath, 'src');
374
+ if (await this.fs.exists(srcPath)) {
375
+ const entries = await this.fs.readdirNames(srcPath);
376
+ const moduleCount = entries.filter(e => !e.startsWith('.')).length;
377
+ if (rule.id === 'F1-R02') {
378
+ if (moduleCount < 2) {
379
+ issues.push({
380
+ ruleId: rule.id,
381
+ severity: rule.severity,
382
+ category: rule.category,
383
+ title: rule.title,
384
+ description: `${rule.description} - Found only ${moduleCount} module(s) in src/`,
385
+ blocking: rule.blocking,
386
+ });
387
+ }
388
+ }
389
+ }
390
+ }
391
+ async validateHexagonalRule(satellitePath, rule, issues) {
392
+ if (rule.id === 'F1-R03') {
393
+ const srcPath = path.join(satellitePath, 'src');
394
+ const hasPorts = await this.fs.exists(path.join(srcPath, 'ports')) ||
395
+ await this.fs.exists(path.join(srcPath, 'Ports')) ||
396
+ await this.fs.exists(path.join(srcPath, 'application/ports'));
397
+ if (!hasPorts) {
398
+ issues.push({
399
+ ruleId: rule.id,
400
+ severity: rule.severity,
401
+ category: rule.category,
402
+ title: rule.title,
403
+ description: `${rule.description} - No ports directory found (expected: src/ports or src/application/ports)`,
404
+ blocking: rule.blocking,
405
+ });
406
+ }
407
+ }
408
+ }
409
+ async validateCommunicationRule(satellitePath, rule, issues) {
410
+ if (rule.id === 'F1-R04' || rule.id === 'F2-R03') {
411
+ const contractsPath = path.join(satellitePath, 'contracts');
412
+ const hasContracts = await this.fs.exists(contractsPath);
413
+ if (!hasContracts) {
414
+ issues.push({
415
+ ruleId: rule.id,
416
+ severity: rule.severity,
417
+ category: rule.category,
418
+ title: rule.title,
419
+ description: `${rule.description} - No contracts/ directory found for inter-module contracts`,
420
+ blocking: rule.blocking,
421
+ });
422
+ }
423
+ }
424
+ }
425
+ async validatePersistenceRule(satellitePath, rule, issues) {
426
+ if (rule.id === 'F1-R05') {
427
+ const aclPath = path.join(satellitePath, 'acl');
428
+ if (!await this.fs.exists(aclPath)) {
429
+ issues.push({
430
+ ruleId: rule.id,
431
+ severity: rule.severity,
432
+ category: rule.category,
433
+ title: rule.title,
434
+ description: `${rule.description} - No acl/ directory found (should contain one subdirectory per bounded context)`,
435
+ blocking: rule.blocking,
436
+ });
437
+ }
438
+ else {
439
+ const entries = await this.fs.readdirNames(aclPath);
440
+ if (entries.length < 2) {
441
+ issues.push({
442
+ ruleId: rule.id,
443
+ severity: rule.severity,
444
+ category: rule.category,
445
+ title: rule.title,
446
+ description: `${rule.description} - Found ${entries.length} bounded context(s) in acl/ (expected multiple)`,
447
+ blocking: rule.blocking,
448
+ });
449
+ }
450
+ }
451
+ }
452
+ }
453
+ async validateAsyncRule(satellitePath, rule, issues) {
454
+ if (rule.id === 'F1-R06') {
455
+ const eventsPath = path.join(satellitePath, 'events') ||
456
+ path.join(satellitePath, 'src', 'events') ||
457
+ path.join(satellitePath, 'src', 'domain', 'events');
458
+ const hasEvents = await this.fs.exists(eventsPath);
459
+ if (!hasEvents) {
460
+ issues.push({
461
+ ruleId: rule.id,
462
+ severity: rule.severity,
463
+ category: rule.category,
464
+ title: rule.title,
465
+ description: `${rule.description} - No events directory found`,
466
+ blocking: rule.blocking,
467
+ });
468
+ }
469
+ }
470
+ }
471
+ async validateExtractionReadinessRule(satellitePath, rule, issues) {
472
+ const extractionReadinessPath = path.join(satellitePath, 'docs', 'extraction-readiness.md');
473
+ const hasExtractionReadiness = await this.fs.exists(extractionReadinessPath);
474
+ if (!hasExtractionReadiness) {
475
+ issues.push({
476
+ ruleId: rule.id,
477
+ severity: rule.severity,
478
+ category: rule.category,
479
+ title: rule.title,
480
+ description: `${rule.description} - No extraction-readiness.md found in docs/`,
481
+ blocking: rule.blocking,
482
+ });
483
+ }
484
+ }
485
+ async validateObservabilityRule(satellitePath, rule, issues) {
486
+ const hasPkgJson = await this.fs.exists(path.join(satellitePath, 'package.json'));
487
+ if (hasPkgJson) {
488
+ const otelConfigPath = path.join(satellitePath, 'otel.config.js') ||
489
+ path.join(satellitePath, 'opentelemetry.config.js') ||
490
+ path.join(satellitePath, 'src', 'instrumentation.ts');
491
+ let hasOtel = false;
492
+ for (const p of [otelConfigPath]) {
493
+ if (await this.fs.exists(p)) {
494
+ hasOtel = true;
495
+ break;
496
+ }
497
+ }
498
+ if (!hasOtel) {
499
+ issues.push({
500
+ ruleId: rule.id,
501
+ severity: rule.severity,
502
+ category: rule.category,
503
+ title: rule.title,
504
+ description: `${rule.description} - No OpenTelemetry instrumentation found`,
505
+ blocking: rule.blocking,
506
+ });
507
+ }
508
+ }
509
+ }
510
+ async validateModuleAutonomyRule(satellitePath, rule, issues) {
511
+ if (rule.id === 'F2-R01') {
512
+ const srcPath = path.join(satellitePath, 'src');
513
+ if (await this.fs.exists(srcPath)) {
514
+ const entries = await this.fs.readdirNames(srcPath);
515
+ let hasIndependentModules = false;
516
+ for (const entry of entries) {
517
+ if (await this.fs.exists(path.join(srcPath, entry, 'package.json'))) {
518
+ hasIndependentModules = true;
519
+ break;
520
+ }
521
+ }
522
+ if (!hasIndependentModules && entries.length > 1) {
523
+ issues.push({
524
+ ruleId: rule.id,
525
+ severity: rule.severity,
526
+ category: rule.category,
527
+ title: rule.title,
528
+ description: `${rule.description} - No independent module package.json files found`,
529
+ blocking: rule.blocking,
530
+ });
531
+ }
532
+ }
533
+ }
534
+ }
535
+ async validateContractStabilityRule(satellitePath, rule, issues) {
536
+ if (rule.id === 'F2-R02') {
537
+ const contractsPath = path.join(satellitePath, 'contracts');
538
+ const hasContracts = await this.fs.exists(contractsPath);
539
+ if (!hasContracts) {
540
+ issues.push({
541
+ ruleId: rule.id,
542
+ severity: rule.severity,
543
+ category: rule.category,
544
+ title: rule.title,
545
+ description: `${rule.description} - No contracts directory found`,
546
+ blocking: rule.blocking,
547
+ });
548
+ }
549
+ else {
550
+ const contractFiles = (await this.fs.readdirNames(contractsPath)).filter(f => f.endsWith('.proto') || f.endsWith('.avsc') || f.endsWith('.json'));
551
+ if (contractFiles.length === 0) {
552
+ issues.push({
553
+ ruleId: rule.id,
554
+ severity: rule.severity,
555
+ category: rule.category,
556
+ title: rule.title,
557
+ description: `${rule.description} - No contract definition files (.proto, .avsc, .json schema) found`,
558
+ blocking: rule.blocking,
559
+ });
560
+ }
561
+ }
562
+ }
563
+ }
564
+ validateDataOwnershipRule(satellitePath, rule, issues) {
565
+ if (rule.id === 'F2-R03') {
566
+ const aclPath = path.join(satellitePath, 'acl');
567
+ if (!this.fs.existsSync(aclPath)) {
568
+ issues.push({
569
+ ruleId: rule.id,
570
+ severity: rule.severity,
571
+ category: rule.category,
572
+ title: rule.title,
573
+ description: `${rule.description} - No acl directory for data ownership enforcement`,
574
+ blocking: rule.blocking,
575
+ });
576
+ }
577
+ }
578
+ }
579
+ async validateAsyncCommunicationRule(satellitePath, rule, issues) {
580
+ if (rule.id === 'F2-R04') {
581
+ const eventsPath = path.join(satellitePath, 'events') || path.join(satellitePath, 'src', 'events');
582
+ const hasEvents = await this.fs.exists(eventsPath);
583
+ if (hasEvents) {
584
+ const eventFiles = (await this.fs.readdirNames(eventsPath)).filter(f => f.endsWith('.json') || f.endsWith('.schema.json'));
585
+ if (eventFiles.length === 0) {
586
+ issues.push({
587
+ ruleId: rule.id,
588
+ severity: rule.severity,
589
+ category: rule.category,
590
+ title: rule.title,
591
+ description: `${rule.description} - No schema-validated event files found`,
592
+ blocking: rule.blocking,
593
+ });
594
+ }
595
+ }
596
+ }
597
+ }
598
+ async validateDistributedTracingRule(satellitePath, rule, issues) {
599
+ const tracerSetupFiles = [
600
+ path.join(satellitePath, 'src', 'tracing.ts'),
601
+ path.join(satellitePath, 'src', 'instrumentation.ts'),
602
+ path.join(satellitePath, 'opentelemetry.config.js'),
603
+ ];
604
+ const existsResults = await Promise.all(tracerSetupFiles.map(f => this.fs.exists(f)));
605
+ const hasTracerSetup = existsResults.some(Boolean);
606
+ if (!hasTracerSetup) {
607
+ issues.push({
608
+ ruleId: rule.id,
609
+ severity: rule.severity,
610
+ category: rule.category,
611
+ title: rule.title,
612
+ description: `${rule.description} - No distributed tracing setup found`,
613
+ blocking: rule.blocking,
614
+ });
615
+ }
616
+ }
617
+ async validateContainerizationRule(satellitePath, rule, issues) {
618
+ const dockerfilePath = path.join(satellitePath, 'Dockerfile');
619
+ const dockerfileExists = await this.fs.exists(dockerfilePath);
620
+ if (rule.id === 'F3-R01' && !dockerfileExists) {
621
+ issues.push({
622
+ ruleId: rule.id,
623
+ severity: rule.severity,
624
+ category: rule.category,
625
+ title: rule.title,
626
+ description: `${rule.description} - No Dockerfile found at repository root`,
627
+ blocking: rule.blocking,
628
+ });
629
+ }
630
+ }
631
+ async validateServiceBoundariesRule(satellitePath, rule, issues) {
632
+ const srcPath = path.join(satellitePath, 'src');
633
+ if (await this.fs.exists(srcPath)) {
634
+ const entries = await this.fs.readdirNames(srcPath);
635
+ const dirEntries = [];
636
+ for (const entry of entries) {
637
+ const entryPath = path.join(srcPath, entry);
638
+ const stat = await this.fs.stat(entryPath);
639
+ if (stat.isDirectory && stat.isDirectory()) {
640
+ dirEntries.push(entry);
641
+ }
642
+ }
643
+ if (rule.id === 'F3-R02' && dirEntries.length < 2) {
644
+ issues.push({
645
+ ruleId: rule.id,
646
+ severity: rule.severity,
647
+ category: rule.category,
648
+ title: rule.title,
649
+ description: `${rule.description} - Only ${dirEntries.length} service(s) found (expected multiple independent services)`,
650
+ blocking: rule.blocking,
651
+ });
652
+ }
653
+ }
654
+ }
261
655
  }
262
656
  exports.RulesetValidatorService = RulesetValidatorService;
263
657
  //# sourceMappingURL=ruleset-validator.service.js.map