arcvision 0.2.17 → 0.2.20

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 (63) hide show
  1. package/.arcvision/logs/errors.log +5 -0
  2. package/arcvision_context/architecture.authority.ledger.json +6 -63
  3. package/bin/arcvision.js +12 -0
  4. package/package.json +3 -2
  5. package/src/core/artifact-manager.js +143 -0
  6. package/src/core/command-base.js +107 -0
  7. package/src/core/config-validator.js +199 -0
  8. package/src/core/error-handler.js +106 -0
  9. package/src/core/feature-manager.js +218 -0
  10. package/src/core/feedback-generator.js +260 -0
  11. package/src/core/invariant-analyzer.js +22 -2
  12. package/src/core/invariant-detector.js +236 -3
  13. package/src/core/parser.js +85 -1
  14. package/src/core/scanner.js +18 -6
  15. package/src/engine/context_builder.js +21 -3
  16. package/src/engine/context_validator.js +7 -1
  17. package/src/engine/pass1_facts.js +2 -2
  18. package/src/index.js +41 -13
  19. package/test-block-functionality.js +40 -0
  20. package/test-dev-project/.arcvision/invariants.json +19 -0
  21. package/{arcvision_context → test-dev-project/arcvision_context}/README.md +9 -9
  22. package/test-dev-project/arcvision_context/architecture.authority.ledger.json +45 -0
  23. package/{arcvision.context.json → test-dev-project/arcvision_context/arcvision.context.json} +498 -496
  24. package/test-dev-project/src/core/data-service.js +0 -0
  25. package/test-dev-project/src/ui/user-profile.js +0 -0
  26. package/test-dev-project/src/utils/helpers.js +0 -0
  27. package/ARCVISION_DIRECTORY_STRUCTURE.md +0 -104
  28. package/CLI_STRUCTURE.md +0 -110
  29. package/CONFIGURATION.md +0 -119
  30. package/IMPLEMENTATION_SUMMARY.md +0 -99
  31. package/README.md +0 -149
  32. package/architecture.authority.ledger.json +0 -46
  33. package/arcvision-0.2.3.tgz +0 -0
  34. package/arcvision-0.2.4.tgz +0 -0
  35. package/arcvision-0.2.5.tgz +0 -0
  36. package/arcvision.context.diff.json +0 -2181
  37. package/arcvision.context.v1.json +0 -2163
  38. package/arcvision.context.v2.json +0 -2173
  39. package/arcvision_context/arcvision.context.json +0 -6884
  40. package/debug-cycle-detection.js +0 -56
  41. package/docs/ENHANCED_ACCURACY_SAFETY_PROTOCOL.md +0 -172
  42. package/docs/accuracy-enhancement-artifacts/enhanced-validation-config.json +0 -98
  43. package/docs/acig-robustness-guide.md +0 -164
  44. package/docs/authoritative-gate-implementation.md +0 -168
  45. package/docs/blast-radius-implementation.md +0 -76
  46. package/docs/blast-radius.md +0 -44
  47. package/docs/cli-strengthening-summary.md +0 -232
  48. package/docs/invariant-system-summary.md +0 -100
  49. package/docs/invariant-system.md +0 -112
  50. package/generate_large_test.js +0 -42
  51. package/large_test_repo.json +0 -1
  52. package/output1.json +0 -2163
  53. package/output2.json +0 -2163
  54. package/scan_calcom_report.txt +0 -0
  55. package/scan_leafmint_report.txt +0 -0
  56. package/scan_output.txt +0 -0
  57. package/scan_trigger_report.txt +0 -0
  58. package/temp_original.js +0 -0
  59. package/test/determinism-test.js +0 -83
  60. package/test-authoritative-context.js +0 -53
  61. package/test-real-authoritative-context.js +0 -118
  62. package/test-upload-enhancements.js +0 -111
  63. package/verify_engine.js +0 -116
@@ -27,6 +27,7 @@ class InvariantDetector {
27
27
  await this.analyzeNodesForInvariants(scanResult.nodes, directory);
28
28
  await this.analyzeEdgesForInvariants(scanResult.edges);
29
29
  await this.analyzeCodeFilesForInvariants(directory);
30
+ await this.analyzeStructuralPatterns(scanResult.nodes, scanResult.edges, directory);
30
31
 
31
32
  console.log(`\n🛡️ Invariant Detection Complete`);
32
33
  console.log(` Found ${this.detectedInvariants.length} potential system invariants`);
@@ -57,11 +58,17 @@ class InvariantDetector {
57
58
  const nodeId = node.path || node.id || 'unknown';
58
59
  this.detectedInvariants.push({
59
60
  id: this.generateInvariantId(invariant.pattern, nodeId),
61
+ system: 'automatic',
60
62
  statement: invariant.description,
61
63
  description: invariant.description,
62
- scope: this.determineScopeFromPath(nodeId),
64
+ severity: invariant.isCritical ? 'block' : 'risk',
65
+ scope: { files: [nodeId] },
63
66
  critical_path: invariant.isCritical,
64
67
  assertion: `Pattern: ${invariant.pattern}`,
68
+ rule: {
69
+ type: 'pattern',
70
+ condition: { pattern: invariant.pattern }
71
+ },
65
72
  status: 'suspected',
66
73
  confidence: this.calculateConfidenceScore(invariant.pattern, nodeId, content),
67
74
  source_file: nodeId,
@@ -86,11 +93,22 @@ class InvariantDetector {
86
93
  for (const constraint of dependencyConstraints) {
87
94
  this.detectedInvariants.push({
88
95
  id: this.generateInvariantId('dependency_constraint', constraint.from + '_' + constraint.to),
96
+ system: 'automatic',
89
97
  statement: constraint.description,
90
98
  description: constraint.description,
91
- scope: 'boundary',
99
+ severity: constraint.critical ? 'block' : 'risk',
100
+ scope: { files: [constraint.from, constraint.to] },
92
101
  critical_path: constraint.critical,
93
102
  assertion: `Dependency constraint: ${constraint.from} -> ${constraint.to}`,
103
+ rule: {
104
+ type: 'dependency',
105
+ condition: {
106
+ forbiddenDependency: {
107
+ from: constraint.from,
108
+ to: constraint.to
109
+ }
110
+ }
111
+ },
94
112
  status: 'suspected',
95
113
  confidence: this.calculateConfidenceScore('dependency_constraint', constraint.from, ''),
96
114
  source_relationship: `${constraint.from} -> ${constraint.to}`,
@@ -116,11 +134,17 @@ class InvariantDetector {
116
134
  for (const pattern of patterns) {
117
135
  this.detectedInvariants.push({
118
136
  id: this.generateInvariantId(pattern.type, relativePath),
137
+ system: 'automatic',
119
138
  statement: `System invariant implemented in ${relativePath}: ${pattern.description}`,
120
139
  description: `System invariant implemented in ${relativePath}: ${pattern.description}`,
121
- scope: 'system',
140
+ severity: 'risk',
141
+ scope: { files: [relativePath] },
122
142
  critical_path: true, // File specifically for invariants is likely critical
123
143
  assertion: pattern.assertion,
144
+ rule: {
145
+ type: 'pattern',
146
+ condition: { pattern: pattern.type }
147
+ },
124
148
  status: 'suspected',
125
149
  confidence: this.calculateConfidenceScore(pattern.type, relativePath, content),
126
150
  source_file: relativePath,
@@ -543,6 +567,215 @@ class InvariantDetector {
543
567
 
544
568
  return baseName;
545
569
  }
570
+
571
+ /**
572
+ * Analyze structural patterns in the codebase and generate architectural invariants
573
+ */
574
+ async analyzeStructuralPatterns(nodes, edges, directory) {
575
+ // Generate layer-based architectural invariants
576
+ await this.generateLayerInvariants(nodes, edges);
577
+
578
+ // Generate dependency-based architectural invariants
579
+ await this.generateDependencyInvariants(nodes, edges);
580
+
581
+ // Generate coupling-based architectural invariants
582
+ await this.generateCouplingInvariants(nodes, edges);
583
+ }
584
+
585
+ /**
586
+ * Generate layer-based architectural invariants
587
+ */
588
+ async generateLayerInvariants(nodes, edges) {
589
+ const layerMap = new Map();
590
+
591
+ // Classify nodes by layer based on path patterns
592
+ for (const node of nodes) {
593
+ const filePath = node.path || node.id;
594
+ if (!filePath) continue;
595
+
596
+ let layer = 'general';
597
+ if (filePath.includes('/controllers/') || filePath.includes('/handlers/')) {
598
+ layer = 'controllers';
599
+ } else if (filePath.includes('/services/') || filePath.includes('/business/')) {
600
+ layer = 'services';
601
+ } else if (filePath.includes('/models/') || filePath.includes('/entities/') || filePath.includes('/schemas/')) {
602
+ layer = 'models';
603
+ } else if (filePath.includes('/utils/') || filePath.includes('/helpers/') || filePath.includes('/lib/')) {
604
+ layer = 'utilities';
605
+ } else if (filePath.includes('/config/') || filePath.includes('/constants/')) {
606
+ layer = 'configuration';
607
+ } else if (filePath.includes('/views/') || filePath.includes('/components/') || filePath.includes('/ui/')) {
608
+ layer = 'presentation';
609
+ }
610
+
611
+ if (!layerMap.has(layer)) {
612
+ layerMap.set(layer, []);
613
+ }
614
+ layerMap.get(layer).push(filePath);
615
+ }
616
+
617
+ // Generate invariants based on layer dependencies (e.g., models shouldn't depend on controllers)
618
+ const layerDependencies = new Map();
619
+ for (const edge of edges) {
620
+ const fromNode = nodes.find(n => n.id === edge.from || n.path === edge.from);
621
+ const toNode = nodes.find(n => n.id === edge.to || n.path === edge.to);
622
+
623
+ if (fromNode && toNode) {
624
+ const fromPath = fromNode.path || fromNode.id;
625
+ const toPath = toNode.path || toNode.id;
626
+
627
+ let fromLayer = 'general';
628
+ let toLayer = 'general';
629
+
630
+ for (const [layer, paths] of layerMap.entries()) {
631
+ if (paths.includes(fromPath)) fromLayer = layer;
632
+ if (paths.includes(toPath)) toLayer = layer;
633
+ }
634
+
635
+ if (fromLayer !== toLayer) {
636
+ const depKey = `${fromLayer}->${toLayer}`;
637
+ if (!layerDependencies.has(depKey)) {
638
+ layerDependencies.set(depKey, []);
639
+ }
640
+ layerDependencies.get(depKey).push({ from: fromPath, to: toPath });
641
+ }
642
+ }
643
+ }
644
+
645
+ // Create invariants for problematic layer dependencies
646
+ for (const [depKey, dependencies] of layerDependencies.entries()) {
647
+ const [fromLayer, toLayer] = depKey.split('->');
648
+
649
+ // Define architectural rules (e.g., models should not depend on controllers)
650
+ const forbiddenLayerDeps = [
651
+ { from: 'models', to: 'controllers' },
652
+ { from: 'models', to: 'services' },
653
+ { from: 'controllers', to: 'models' },
654
+ { from: 'presentation', to: 'models' }
655
+ ];
656
+
657
+ const isForbidden = forbiddenLayerDeps.some(rule =>
658
+ rule.from === fromLayer && rule.to === toLayer
659
+ );
660
+
661
+ if (isForbidden) {
662
+ this.detectedInvariants.push({
663
+ id: this.generateInvariantId(`layer_${fromLayer}_not_depend_${toLayer}`, `${fromLayer}_${toLayer}`),
664
+ system: 'architectural',
665
+ statement: `Layer dependency rule: ${fromLayer} components should not depend on ${toLayer} components`,
666
+ description: `Prevent ${fromLayer} layer from depending on ${toLayer} layer to maintain separation of concerns`,
667
+ severity: 'block',
668
+ scope: { files: dependencies.map(d => d.from) },
669
+ critical_path: true,
670
+ assertion: `Layer dependency rule: ${fromLayer} -> ${toLayer} is forbidden`,
671
+ rule: {
672
+ type: 'dependency',
673
+ condition: {
674
+ forbiddenDependency: {
675
+ from: `**/${fromLayer}/**`,
676
+ to: `**/${toLayer}/**`
677
+ }
678
+ }
679
+ },
680
+ status: 'detected',
681
+ confidence: 0.9,
682
+ detected_at: new Date().toISOString()
683
+ });
684
+ }
685
+ }
686
+ }
687
+
688
+ /**
689
+ * Generate dependency-based architectural invariants
690
+ */
691
+ async generateDependencyInvariants(nodes, edges) {
692
+ // Identify high-degree nodes that might indicate architectural hubs
693
+ const nodeDegrees = new Map();
694
+
695
+ for (const edge of edges) {
696
+ // Count incoming edges (dependencies on this node)
697
+ if (!nodeDegrees.has(edge.to)) {
698
+ nodeDegrees.set(edge.to, { incoming: 0, outgoing: 0 });
699
+ }
700
+ if (!nodeDegrees.has(edge.from)) {
701
+ nodeDegrees.set(edge.from, { incoming: 0, outgoing: 0 });
702
+ }
703
+
704
+ nodeDegrees.get(edge.to).incoming++;
705
+ nodeDegrees.get(edge.from).outgoing++;
706
+ }
707
+
708
+ // Identify potential architectural bottlenecks
709
+ for (const [nodeId, degrees] of nodeDegrees.entries()) {
710
+ if (degrees.incoming > 10) { // High incoming degree indicates hub
711
+ this.detectedInvariants.push({
712
+ id: this.generateInvariantId(`hub_protection_${nodeId}`, 'high_degree'),
713
+ system: 'architectural',
714
+ statement: `Protect high-degree node ${nodeId} from unauthorized modifications`,
715
+ description: `Node ${nodeId} has ${degrees.incoming} incoming dependencies, changes here affect many components`,
716
+ severity: 'risk',
717
+ scope: { files: [nodeId] },
718
+ critical_path: true,
719
+ assertion: `High-degree node protection: ${nodeId} has ${degrees.incoming} dependencies`,
720
+ rule: {
721
+ type: 'access_control',
722
+ condition: {
723
+ mustBeReviewed: true
724
+ }
725
+ },
726
+ status: 'detected',
727
+ confidence: 0.8,
728
+ detected_at: new Date().toISOString()
729
+ });
730
+ }
731
+ }
732
+ }
733
+
734
+ /**
735
+ * Generate coupling-based architectural invariants
736
+ */
737
+ async generateCouplingInvariants(nodes, edges) {
738
+ // Identify tightly coupled components
739
+ const couplingMap = new Map();
740
+
741
+ for (const edge of edges) {
742
+ // Look for mutual dependencies indicating tight coupling
743
+ const reverseEdge = edges.find(e => e.from === edge.to && e.to === edge.from);
744
+
745
+ if (reverseEdge) {
746
+ const coupleKey = [edge.from, edge.to].sort().join('|');
747
+ if (!couplingMap.has(coupleKey)) {
748
+ couplingMap.set(coupleKey, []);
749
+ }
750
+ couplingMap.get(coupleKey).push(edge);
751
+ }
752
+ }
753
+
754
+ // Create invariants for tightly coupled components
755
+ for (const [coupleKey, edgesList] of couplingMap.entries()) {
756
+ const [nodeA, nodeB] = coupleKey.split('|');
757
+
758
+ this.detectedInvariants.push({
759
+ id: this.generateInvariantId(`tight_coupling_${nodeA}_${nodeB}`, 'bidirectional'),
760
+ system: 'architectural',
761
+ statement: `Prevent tight coupling between ${nodeA} and ${nodeB}`,
762
+ description: `Bidirectional dependency detected between ${nodeA} and ${nodeB}, indicating tight coupling`,
763
+ severity: 'risk',
764
+ scope: { files: [nodeA, nodeB] },
765
+ critical_path: false,
766
+ assertion: `Bidirectional dependency: ${nodeA} <-> ${nodeB}`,
767
+ rule: {
768
+ type: 'dependency',
769
+ condition: {
770
+ shouldAvoidBidirectional: true
771
+ }
772
+ },
773
+ status: 'detected',
774
+ confidence: 0.7,
775
+ detected_at: new Date().toISOString()
776
+ });
777
+ }
778
+ }
546
779
  }
547
780
 
548
781
  module.exports = { InvariantDetector };
@@ -220,7 +220,13 @@ function parseFile(filePath) {
220
220
  classes: [],
221
221
  variables: [],
222
222
  types: [],
223
- dependencies: []
223
+ dependencies: [],
224
+ functionCalls: [],
225
+ methodCalls: [],
226
+ constructorCalls: [],
227
+ typeImports: [],
228
+ interfaceDependencies: [],
229
+ genericDependencies: []
224
230
  };
225
231
 
226
232
  traverse(ast, {
@@ -421,6 +427,55 @@ function parseFile(filePath) {
421
427
  loc: node.loc
422
428
  });
423
429
  }
430
+
431
+ // Track type imports/usage for dependency analysis
432
+ if (node.typeParameters && node.typeParameters.params) {
433
+ node.typeParameters.params.forEach(param => {
434
+ if (param.constraint) {
435
+ metadata.genericDependencies = metadata.genericDependencies || [];
436
+ metadata.genericDependencies.push({
437
+ name: param.name,
438
+ constraint: param.constraint,
439
+ loc: param.loc
440
+ });
441
+ }
442
+ });
443
+ }
444
+ },
445
+
446
+ TSInterfaceDeclaration({ node }) {
447
+ if (node.id && node.id.name) {
448
+ metadata.types.push({
449
+ name: node.id.name,
450
+ kind: 'interface',
451
+ loc: node.loc
452
+ });
453
+ }
454
+
455
+ // Track interface inheritance for dependency analysis
456
+ if (node.extends) {
457
+ metadata.interfaceDependencies = metadata.interfaceDependencies || [];
458
+ node.extends.forEach(extend => {
459
+ if (extend.expression && extend.expression.name) {
460
+ metadata.interfaceDependencies.push({
461
+ name: extend.expression.name,
462
+ type: 'extends',
463
+ loc: extend.loc
464
+ });
465
+ }
466
+ });
467
+ }
468
+ },
469
+
470
+ TSImportType({ node }) {
471
+ // Track TypeScript import types
472
+ metadata.typeImports = metadata.typeImports || [];
473
+ if (node.argument && node.argument.value) {
474
+ metadata.typeImports.push({
475
+ source: node.argument.value,
476
+ loc: node.loc
477
+ });
478
+ }
424
479
  },
425
480
  VariableDeclaration({ node }) {
426
481
  node.declarations.forEach(decl => {
@@ -513,6 +568,35 @@ function parseFile(filePath) {
513
568
  });
514
569
  }
515
570
  }
571
+
572
+ // Enhanced call detection for better structural relationship mapping
573
+ if (node.callee.type === 'Identifier') {
574
+ // Track function calls for dependency analysis
575
+ metadata.functionCalls = metadata.functionCalls || [];
576
+ metadata.functionCalls.push({
577
+ name: node.callee.name,
578
+ loc: node.loc
579
+ });
580
+ } else if (node.callee.type === 'MemberExpression') {
581
+ // Track method calls
582
+ metadata.methodCalls = metadata.methodCalls || [];
583
+ const objectName = node.callee.object.name || (node.callee.object.property ? node.callee.object.property.name : 'unknown');
584
+ const methodName = node.callee.property.name || 'unknown';
585
+ metadata.methodCalls.push({
586
+ object: objectName,
587
+ method: methodName,
588
+ loc: node.loc
589
+ });
590
+ }
591
+
592
+ // Track constructor calls
593
+ if (node.callee.type === 'Identifier' && node.callee.name && /^[A-Z]/.test(node.callee.name)) {
594
+ metadata.constructorCalls = metadata.constructorCalls || [];
595
+ metadata.constructorCalls.push({
596
+ constructor: node.callee.name,
597
+ loc: node.loc
598
+ });
599
+ }
516
600
  },
517
601
 
518
602
  VariableDeclarator({ node }) {
@@ -148,10 +148,20 @@ async function scan(directory) {
148
148
  });
149
149
 
150
150
  // Assess architectural health based on invariants
151
- architecturalHealth = invariantAnalyzer.assessArchitecturalHealth(detectedInvariants, {
152
- directory,
153
- projectName: path.basename(directory)
154
- });
151
+ try {
152
+ architecturalHealth = invariantAnalyzer.assessArchitecturalHealth(detectedInvariants, {
153
+ directory,
154
+ projectName: path.basename(directory)
155
+ });
156
+ } catch (healthError) {
157
+ console.warn(`⚠️ Could not assess architectural health: ${healthError.message}`);
158
+ // Provide a default health assessment
159
+ architecturalHealth = {
160
+ score: 0,
161
+ level: 'unknown',
162
+ assessment: 'Could not assess architectural health due to analysis error'
163
+ };
164
+ }
155
165
  } else {
156
166
  // Even if no invariants are detected, create a baseline analysis
157
167
  const invariantAnalyzer = new InvariantAnalyzer();
@@ -186,7 +196,7 @@ async function scan(directory) {
186
196
  structuralInvariants,
187
197
  authoritativeContext,
188
198
  // Include detected invariants
189
- detectedInvariants,
199
+ autoDetectedInvariants: detectedInvariants,
190
200
  // Include invariant analysis results
191
201
  invariantAnalysis,
192
202
  // Include architectural health assessment
@@ -227,7 +237,7 @@ async function scan(directory) {
227
237
  }
228
238
 
229
239
  // Build context and ensure archetype is properly set
230
- let context = buildContext(nodes, edges, finalContextOptions);
240
+ let context = buildContext(nodes, edges, symbols, finalContextOptions);
231
241
 
232
242
  // Make sure archetype is set properly in the system section
233
243
  if (!context.system.archetype || context.system.archetype === null) {
@@ -250,6 +260,8 @@ async function scan(directory) {
250
260
  console.warn(' ⚠️ Proceeding despite validation issues to see results');
251
261
  // Continue processing instead of throwing error
252
262
  // throw new Error("Context validation failed");
263
+ } else {
264
+ console.log(' ✅ Context validation passed');
253
265
  }
254
266
 
255
267
  // Deterministic sort for consistent hashing/snapshots
@@ -257,9 +257,27 @@ function buildContext(files, edges, options = {}) {
257
257
  context.metrics.files_with_high_blast_radius = nodes.filter(n => n.blast_radius > 5).length;
258
258
  context.metrics.total_dependencies = schemaEdges.length;
259
259
 
260
- // Add new canonical sections if provided in options
261
- if (options.invariants !== undefined) {
262
- context.invariants = options.invariants;
260
+ // Include automatically detected invariants in the context first
261
+ if (options.autoDetectedInvariants !== undefined && Array.isArray(options.autoDetectedInvariants) && options.autoDetectedInvariants.length > 0) {
262
+ context.auto_detected_invariants = options.autoDetectedInvariants;
263
+
264
+ // Initialize context.invariants with auto-detected invariants if not already set
265
+ if (!context.invariants || !Array.isArray(context.invariants)) {
266
+ context.invariants = [...options.autoDetectedInvariants];
267
+ } else {
268
+ // Merge with existing invariants if they exist
269
+ context.invariants = [...context.invariants, ...options.autoDetectedInvariants];
270
+ }
271
+ }
272
+
273
+ // Add new canonical sections if provided in options - this will add Pass 4 invariants if they exist
274
+ if (options.invariants !== undefined && Array.isArray(options.invariants) && options.invariants.length > 0) {
275
+ if (!context.invariants) {
276
+ context.invariants = options.invariants;
277
+ } else if (Array.isArray(context.invariants)) {
278
+ // Merge with existing invariants (could be from autoDetectedInvariants)
279
+ context.invariants = [...context.invariants, ...options.invariants];
280
+ }
263
281
  }
264
282
 
265
283
  if (options.ownership !== undefined) {
@@ -2,8 +2,14 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const Ajv = require('ajv');
4
4
 
5
+ // Import format validators
6
+ const addFormats = require('ajv-formats');
7
+
5
8
  // Create AJV instance with options for detailed error reporting
6
- const ajv = new Ajv({ allErrors: true, strict: false });
9
+ const ajv = new Ajv({ allErrors: true, strict: false, validateFormats: false, logger: false });
10
+
11
+ // Add standard formats including date-time
12
+ addFormats(ajv);
7
13
 
8
14
  /**
9
15
  * Validate the context object against the Arcvision schema and invariants using AJV
@@ -38,7 +38,7 @@ async function executePass1(directory, options = {}) {
38
38
  }
39
39
 
40
40
  // Find all relevant code files for structural analysis
41
- const files = await glob('**/*.{js,jsx,ts,tsx,cjs,mjs,json,lua}', {
41
+ const files = glob.sync('**/*.{js,jsx,ts,tsx,cjs,mjs,json,lua}', {
42
42
  ...scanOptions,
43
43
  ignore: [...scanOptions.ignore,
44
44
  '**/*.d.ts',
@@ -68,7 +68,7 @@ async function executePass1(directory, options = {}) {
68
68
  });
69
69
 
70
70
  // Use only code files for analysis
71
- const allFiles = files;
71
+ const allFiles = Array.isArray(files) ? files : [];
72
72
 
73
73
  console.log(` Identified ${allFiles.length} files for analysis`);
74
74
 
package/src/index.js CHANGED
@@ -16,7 +16,7 @@ const RetryHandler = require('./core/retry-handler');
16
16
  const { invariantRegistry } = require('./core/invariant-registry');
17
17
  const { evaluateChange } = require('./core/change-evaluator');
18
18
  // Import authority gate components
19
- const { authorityLedger } = require('./core/authority-ledger');
19
+ const { AuthorityLedger } = require('./core/authority-ledger');
20
20
  const { overrideHandler } = require('./core/override-handler');
21
21
  // Import robust validation and error handling
22
22
  const { cliValidator } = require('./core/cli-validator');
@@ -307,6 +307,9 @@ async function uploadToDatabase(jsonData) {
307
307
  controller.abort();
308
308
  clearTimeout(progress30s);
309
309
  clearTimeout(progress60s);
310
+ console.error(chalk.red('\n❌ Upload timeout after 120 seconds'));
311
+ console.error(chalk.red('This may indicate network issues or server problems'));
312
+ console.error(chalk.yellow('Try again later or check your internet connection'));
310
313
  }, 120000); // 120 second timeout
311
314
 
312
315
  const response = await fetch(`${API_URL}/api/upload`, {
@@ -319,6 +322,14 @@ async function uploadToDatabase(jsonData) {
319
322
  graph: jsonData
320
323
  }),
321
324
  signal: controller.signal
325
+ }).catch(error => {
326
+ if (error.name === 'AbortError') {
327
+ throw new Error('Upload cancelled due to timeout');
328
+ }
329
+ if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
330
+ throw new Error(`Cannot connect to ${API_URL} - check your internet connection and that the server is running`);
331
+ }
332
+ throw error;
322
333
  });
323
334
 
324
335
  // Clear all timeouts
@@ -405,6 +416,13 @@ program
405
416
  directory: targetDir
406
417
  });
407
418
 
419
+ // Initialize Artifact Manager
420
+ const { ArtifactManager } = require('./core/artifact-manager');
421
+ const artifactManager = new ArtifactManager(targetDir);
422
+
423
+ // Ensure all required artifacts exist
424
+ artifactManager.ensureArtifacts();
425
+
408
426
  console.log(chalk.blue(`Scanning directory: ${targetDir}`));
409
427
 
410
428
  const map = await scanner.scan(targetDir);
@@ -413,7 +431,7 @@ program
413
431
  // Analyze and print blast radius insight
414
432
  const blastRadiusAnalysis = analyzeBlastRadius(map);
415
433
  if (blastRadiusAnalysis && blastRadiusAnalysis.topFiles && blastRadiusAnalysis.topFiles.length > 0) {
416
- console.log('\n⚠️ Top Structural Context Hubs Detected:\n');
434
+ console.log('\n🔍 STRUCTURAL ANALYSIS RESULTS:\n');
417
435
 
418
436
  blastRadiusAnalysis.topFiles.forEach((item, index) => {
419
437
  let warningMessage = '';
@@ -425,12 +443,12 @@ program
425
443
  warningMessage = 'Modifications can cause widespread inconsistencies.';
426
444
  }
427
445
 
428
- console.log(`${index + 1}. ${item.file}`);
429
- console.log(` Blast Radius: ${item.blastRadius} files (${item.percentOfGraph}%)`);
430
- console.log(` Warning: ${warningMessage}\n`);
446
+ console.log(`${index + 1}. 📁 ${item.file}`);
447
+ console.log(` 🎯 Blast Radius: ${item.blastRadius} files (${item.percentOfGraph}%)`);
448
+ console.log(` ⚠️ Risk: ${warningMessage}\n`);
431
449
  });
432
450
  } else {
433
- console.log('\nNo high-structure files detected based on import dependencies.');
451
+ console.log('\n✅ No high-impact structural hubs detected in this codebase.');
434
452
  }
435
453
 
436
454
  // Validate the map before saving or uploading
@@ -554,18 +572,28 @@ program
554
572
  contextFile: options.contextFile,
555
573
  invariantsFile: options.invariantsFile
556
574
  });
557
-
575
+
576
+ // Initialize Artifact Manager
577
+ const { ArtifactManager } = require('./core/artifact-manager');
578
+ const artifactManager = new ArtifactManager(targetDir);
579
+
580
+ // Ensure all required artifacts exist
581
+ artifactManager.ensureArtifacts();
582
+
583
+ // Initialize Authority Ledger with target directory
584
+ const authorityLedger = new AuthorityLedger(path.join(targetDir, 'arcvision_context', 'architecture.authority.ledger.json'));
585
+
558
586
  console.log(chalk.blue('🔒 Authoritative Change Impact Gate (ACIG)'));
559
587
  console.log(chalk.blue('Evaluating changes against canonical system invariants...\n'));
560
-
588
+
561
589
  // Load current context from arcvision_context directory
562
590
  const contextFile = options.contextFile || path.join(targetDir, 'arcvision_context', 'arcvision.context.json');
563
591
  if (!fs.existsSync(contextFile)) {
564
592
  console.error(chalk.red(`❌ Context file not found: ${contextFile}`));
565
- console.error(chalk.yellow('Run `arcvision scan` first to generate context file'));
593
+ console.error(chalk.yellow('Run \`arcvision scan\` first to generate context file'));
566
594
  process.exit(1);
567
595
  }
568
-
596
+
569
597
  let context;
570
598
  try {
571
599
  const contextContent = fs.readFileSync(contextFile, 'utf8');
@@ -574,13 +602,13 @@ program
574
602
  console.error(chalk.red(`❌ Failed to parse context file: ${parseError.message}`));
575
603
  process.exit(1);
576
604
  }
577
-
605
+
578
606
  console.log(chalk.green(`✅ Loaded context from: ${contextFile}`));
579
-
607
+
580
608
  // Load invariants
581
609
  const invariantsFile = options.invariantsFile || path.join(targetDir, '.arcvision', 'invariants.json');
582
610
  let loadSuccess = false;
583
-
611
+
584
612
  if (fs.existsSync(invariantsFile)) {
585
613
  loadSuccess = invariantRegistry.loadFromFile(invariantsFile);
586
614
  if (loadSuccess) {
@@ -0,0 +1,40 @@
1
+ const { evaluateChange } = require('./src/core/change-evaluator');
2
+
3
+ // Create a test that should definitely trigger a BLOCK
4
+ const mockContext = {
5
+ nodes: [
6
+ {
7
+ path: 'src/ui/user-profile.js',
8
+ content: 'import { fetchData } from "../core/data-service";'
9
+ }
10
+ ]
11
+ };
12
+
13
+ const testInvariant = {
14
+ id: 'block-all-ui-files',
15
+ system: 'test',
16
+ description: 'Block all UI files for testing',
17
+ severity: 'block',
18
+ scope: {
19
+ files: ['**/ui/**/*']
20
+ },
21
+ rule: {
22
+ type: 'existence',
23
+ condition: {
24
+ mustNotExist: 'src/ui/user-profile.js'
25
+ }
26
+ }
27
+ };
28
+
29
+ const result = evaluateChange({
30
+ changedFiles: ['src/ui/user-profile.js'],
31
+ dependencyGraph: mockContext,
32
+ invariants: [testInvariant],
33
+ context: {}
34
+ });
35
+
36
+ console.log('Test result:', result.decision);
37
+ console.log('Violations:', result.violations.length);
38
+ if (result.violations.length > 0) {
39
+ console.log('Violation details:', result.violations[0].description);
40
+ }