arcvision 0.2.26 → 0.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core/artifact-manager.js +42 -13
- package/src/core/failure-mode-synthesizer.js +80 -37
- package/src/core/invariant-detector.js +44 -11
- package/src/core/scanner.js +113 -24
package/package.json
CHANGED
|
@@ -33,9 +33,22 @@ class ArtifactManager {
|
|
|
33
33
|
createDirectories() {
|
|
34
34
|
this.requiredDirs.forEach(dir => {
|
|
35
35
|
const fullPath = path.join(this.projectRoot, dir);
|
|
36
|
-
|
|
37
|
-
fs.
|
|
38
|
-
|
|
36
|
+
try {
|
|
37
|
+
if (!fs.existsSync(fullPath)) {
|
|
38
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
39
|
+
this.logCreation(dir);
|
|
40
|
+
// Verify directory was created
|
|
41
|
+
if (fs.existsSync(fullPath)) {
|
|
42
|
+
console.log(`✅ Verified directory creation: ${fullPath}`);
|
|
43
|
+
} else {
|
|
44
|
+
console.warn(`⚠️ Directory may not have been created: ${fullPath}`);
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
console.log(`✅ Directory already exists: ${fullPath}`);
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error(`❌ Failed to create directory ${fullPath}: ${error.message}`);
|
|
51
|
+
throw error;
|
|
39
52
|
}
|
|
40
53
|
});
|
|
41
54
|
}
|
|
@@ -46,16 +59,32 @@ class ArtifactManager {
|
|
|
46
59
|
createMissingFiles() {
|
|
47
60
|
// Create default ledger if missing
|
|
48
61
|
const ledgerPath = path.join(this.projectRoot, this.requiredFiles.ledger);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
try {
|
|
63
|
+
if (!fs.existsSync(ledgerPath)) {
|
|
64
|
+
console.log(`🔍 Creating ledger file: ${ledgerPath}`);
|
|
65
|
+
const defaultLedger = {
|
|
66
|
+
schema_version: '1.0',
|
|
67
|
+
system_id: path.basename(this.projectRoot),
|
|
68
|
+
created_at: new Date().toISOString(),
|
|
69
|
+
ledger: []
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
fs.writeFileSync(ledgerPath, JSON.stringify(defaultLedger, null, 2));
|
|
73
|
+
this.logCreation(this.requiredFiles.ledger);
|
|
74
|
+
|
|
75
|
+
// Verify file was created
|
|
76
|
+
if (fs.existsSync(ledgerPath)) {
|
|
77
|
+
const stats = fs.statSync(ledgerPath);
|
|
78
|
+
console.log(`✅ Verified ledger file creation: ${ledgerPath} (${stats.size} bytes)`);
|
|
79
|
+
} else {
|
|
80
|
+
console.warn(`⚠️ Ledger file may not have been created: ${ledgerPath}`);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
console.log(`✅ Ledger file already exists: ${ledgerPath}`);
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(`❌ Failed to create ledger file ${ledgerPath}: ${error.message}`);
|
|
87
|
+
throw error;
|
|
59
88
|
}
|
|
60
89
|
}
|
|
61
90
|
|
|
@@ -68,21 +68,35 @@ class FailureModeSynthesizer {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
* Get default symptom for an invariant
|
|
71
|
+
* Get default symptom for an invariant based on its statement
|
|
72
72
|
*/
|
|
73
73
|
getDefaultSymptom(invariant) {
|
|
74
|
-
if (invariant
|
|
75
|
-
return '
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
if (!invariant) {
|
|
75
|
+
return 'System behavior deviates from expected requirements';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Handle cases where invariant might have description but no statement
|
|
79
|
+
const statementText = invariant.statement || invariant.description || invariant.id || '';
|
|
80
|
+
if (!statementText) {
|
|
81
|
+
return 'System behavior deviates from expected requirements';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Ensure we have a string before calling toLowerCase
|
|
85
|
+
const statementStr = String(statementText);
|
|
86
|
+
const statement = statementStr.toLowerCase();
|
|
87
|
+
|
|
88
|
+
if (statement.includes('availability')) {
|
|
89
|
+
return 'UI does not update or service unreachable';
|
|
90
|
+
} else if (statement.includes('state')) {
|
|
91
|
+
return 'Stale or inconsistent data presented to user';
|
|
92
|
+
} else if (statement.includes('sync') || statement.includes('synchronize')) {
|
|
80
93
|
return 'Data inconsistency between components';
|
|
81
|
-
} else if (
|
|
82
|
-
invariant.statement.toLowerCase().includes('auth')) {
|
|
94
|
+
} else if (statement.includes('permission') || statement.includes('auth')) {
|
|
83
95
|
return 'Unauthorized access or denied permissions';
|
|
96
|
+
} else if (statement.includes('performance') || statement.includes('latency')) {
|
|
97
|
+
return 'System response time exceeds threshold';
|
|
84
98
|
} else {
|
|
85
|
-
return 'Unexpected behavior or incorrect output';
|
|
99
|
+
return 'Unexpected behavior or incorrect logical output';
|
|
86
100
|
}
|
|
87
101
|
}
|
|
88
102
|
|
|
@@ -90,17 +104,34 @@ class FailureModeSynthesizer {
|
|
|
90
104
|
* Get blast radius for an invariant based on related nodes
|
|
91
105
|
*/
|
|
92
106
|
getInvariantBlastRadius(invariant, architectureMap) {
|
|
107
|
+
if (!invariant) {
|
|
108
|
+
return ['unknown'];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Handle cases where invariant might have description but no statement
|
|
112
|
+
const statementText = invariant.statement || invariant.description || invariant.id || '';
|
|
113
|
+
if (!statementText) {
|
|
114
|
+
return ['unknown'];
|
|
115
|
+
}
|
|
116
|
+
|
|
93
117
|
const { nodes } = architectureMap;
|
|
94
118
|
const blastRadius = [];
|
|
119
|
+
// Ensure we have a string before calling toLowerCase
|
|
120
|
+
const statementStr = String(statementText);
|
|
121
|
+
const statement = statementStr.toLowerCase();
|
|
95
122
|
|
|
96
123
|
// Look for nodes that are related to the invariant
|
|
97
124
|
for (const node of nodes) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
125
|
+
try {
|
|
126
|
+
if (node.path && statement.includes(node.path.toLowerCase())) {
|
|
127
|
+
blastRadius.push(node.path);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (node.id && statement.includes(node.id.toLowerCase())) {
|
|
131
|
+
blastRadius.push(node.id);
|
|
132
|
+
}
|
|
133
|
+
} catch (e) {
|
|
134
|
+
// Ignore errors
|
|
104
135
|
}
|
|
105
136
|
}
|
|
106
137
|
|
|
@@ -229,16 +260,19 @@ class FailureModeSynthesizer {
|
|
|
229
260
|
*/
|
|
230
261
|
isRelevantToInvariant(source, invariant) {
|
|
231
262
|
// Check if the source location is mentioned in the invariant
|
|
232
|
-
if (invariant.owner && source.location &&
|
|
233
|
-
|
|
263
|
+
if (invariant.owner && source.location &&
|
|
264
|
+
invariant.owner.toLowerCase().includes(source.location.toLowerCase())) {
|
|
234
265
|
return true;
|
|
235
266
|
}
|
|
236
267
|
|
|
237
|
-
// Check if the invariant statement mentions similar concepts
|
|
238
|
-
|
|
239
|
-
|
|
268
|
+
// Check if the invariant statement/description mentions similar concepts
|
|
269
|
+
const statementText = invariant.statement || invariant.description || invariant.id || '';
|
|
270
|
+
if (statementText && source.location) {
|
|
271
|
+
// Ensure we have a string before calling toLowerCase
|
|
272
|
+
const statementStr = String(statementText);
|
|
273
|
+
const invariantWords = statementStr.toLowerCase().split(/\W+/);
|
|
240
274
|
const locationWords = source.location.toLowerCase().split(/[\/\\.-_]+/);
|
|
241
|
-
|
|
275
|
+
|
|
242
276
|
// Check for overlapping terms
|
|
243
277
|
for (const word of invariantWords) {
|
|
244
278
|
if (word.length > 3 && locationWords.includes(word)) {
|
|
@@ -254,16 +288,25 @@ class FailureModeSynthesizer {
|
|
|
254
288
|
* Predict symptoms based on invariant and trigger
|
|
255
289
|
*/
|
|
256
290
|
predictSymptom(invariant, trigger) {
|
|
291
|
+
// Handle cases where invariant might have description but no statement
|
|
292
|
+
const statementText = invariant.statement || invariant.description || invariant.id || '';
|
|
293
|
+
if (!statementText) {
|
|
294
|
+
return 'Unexpected behavior or incorrect output';
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Ensure we have a string before calling toLowerCase
|
|
298
|
+
const statementStr = String(statementText);
|
|
299
|
+
|
|
257
300
|
// Default symptom prediction based on invariant type
|
|
258
|
-
if (
|
|
301
|
+
if (statementStr.toLowerCase().includes('availability')) {
|
|
259
302
|
return 'UI does not update';
|
|
260
|
-
} else if (
|
|
303
|
+
} else if (statementStr.toLowerCase().includes('state')) {
|
|
261
304
|
return 'Stale data presented to user';
|
|
262
|
-
} else if (
|
|
263
|
-
|
|
305
|
+
} else if (statementStr.toLowerCase().includes('sync') ||
|
|
306
|
+
statementStr.toLowerCase().includes('synchronize')) {
|
|
264
307
|
return 'Data inconsistency between components';
|
|
265
|
-
} else if (
|
|
266
|
-
|
|
308
|
+
} else if (statementStr.toLowerCase().includes('permission') ||
|
|
309
|
+
statementStr.toLowerCase().includes('auth')) {
|
|
267
310
|
return 'Unauthorized access or denied permissions';
|
|
268
311
|
} else {
|
|
269
312
|
return 'Unexpected behavior or incorrect output';
|
|
@@ -325,16 +368,16 @@ class FailureModeSynthesizer {
|
|
|
325
368
|
*/
|
|
326
369
|
containsMutationPattern(func) {
|
|
327
370
|
if (!func.name) return false;
|
|
328
|
-
|
|
371
|
+
|
|
329
372
|
const name = func.name.toLowerCase();
|
|
330
|
-
return name.includes('set') ||
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
373
|
+
return name.includes('set') ||
|
|
374
|
+
name.includes('update') ||
|
|
375
|
+
name.includes('modify') ||
|
|
376
|
+
name.includes('change') ||
|
|
377
|
+
name.includes('mutate') ||
|
|
378
|
+
name.includes('assign') ||
|
|
379
|
+
name.includes('put') ||
|
|
380
|
+
name.includes('post');
|
|
338
381
|
}
|
|
339
382
|
}
|
|
340
383
|
|
|
@@ -23,18 +23,45 @@ class InvariantDetector {
|
|
|
23
23
|
this.detectedInvariants = [];
|
|
24
24
|
|
|
25
25
|
try {
|
|
26
|
+
// Validate inputs
|
|
27
|
+
if (!scanResult || !scanResult.nodes || !scanResult.edges) {
|
|
28
|
+
console.warn('⚠️ Invalid scan result provided to invariant detector');
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log('\n🔍 Starting Invariant Detection Analysis');
|
|
33
|
+
console.log(` Analyzing ${scanResult.nodes.length} nodes and ${scanResult.edges.length} relationships`);
|
|
34
|
+
|
|
26
35
|
// Analyze the structural context for potential invariants
|
|
27
36
|
await this.analyzeNodesForInvariants(scanResult.nodes, directory);
|
|
28
37
|
await this.analyzeEdgesForInvariants(scanResult.edges);
|
|
29
38
|
await this.analyzeCodeFilesForInvariants(directory);
|
|
30
39
|
await this.analyzeStructuralPatterns(scanResult.nodes, scanResult.edges, directory);
|
|
31
40
|
|
|
41
|
+
// Filter out any invariants without proper statements
|
|
42
|
+
this.detectedInvariants = this.detectedInvariants.filter(inv => {
|
|
43
|
+
if (!inv.statement || inv.statement.trim() === '') {
|
|
44
|
+
console.warn(`⚠️ Removing invariant ${inv.id} due to missing statement`);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
|
|
32
50
|
console.log(`\n🛡️ Invariant Detection Complete`);
|
|
33
51
|
console.log(` Found ${this.detectedInvariants.length} potential system invariants`);
|
|
52
|
+
|
|
53
|
+
// Log summary of invariant types
|
|
54
|
+
const typeCounts = {};
|
|
55
|
+
this.detectedInvariants.forEach(inv => {
|
|
56
|
+
const type = inv.system || 'unknown';
|
|
57
|
+
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
|
58
|
+
});
|
|
59
|
+
console.log(` Types: ${JSON.stringify(typeCounts)}`);
|
|
34
60
|
|
|
35
61
|
return this.detectedInvariants;
|
|
36
62
|
} catch (error) {
|
|
37
63
|
console.error('Invariant detection failed:', error.message);
|
|
64
|
+
console.error('Stack trace:', error.stack);
|
|
38
65
|
return [];
|
|
39
66
|
}
|
|
40
67
|
}
|
|
@@ -63,7 +90,7 @@ class InvariantDetector {
|
|
|
63
90
|
this.detectedInvariants.push({
|
|
64
91
|
id: invariantId,
|
|
65
92
|
system: 'automatic',
|
|
66
|
-
statement: invariant.description
|
|
93
|
+
statement: invariant.description || `Invariant detected in ${nodeId}`, // Ensure statement exists
|
|
67
94
|
description: invariant.description,
|
|
68
95
|
scope: { files: [nodeId] },
|
|
69
96
|
critical_path: invariant.isCritical,
|
|
@@ -101,7 +128,7 @@ class InvariantDetector {
|
|
|
101
128
|
this.detectedInvariants.push({
|
|
102
129
|
id: this.generateInvariantId('dependency_constraint', constraint.from + '_' + constraint.to),
|
|
103
130
|
system: 'automatic',
|
|
104
|
-
statement: constraint.description
|
|
131
|
+
statement: constraint.description || `Dependency constraint from ${constraint.from} to ${constraint.to}`, // Ensure statement exists
|
|
105
132
|
description: constraint.description,
|
|
106
133
|
scope: { files: [constraint.from, constraint.to] },
|
|
107
134
|
critical_path: constraint.critical,
|
|
@@ -145,7 +172,7 @@ class InvariantDetector {
|
|
|
145
172
|
this.detectedInvariants.push({
|
|
146
173
|
id: this.generateInvariantId(pattern.type, relativePath, i),
|
|
147
174
|
system: 'automatic',
|
|
148
|
-
statement: `System invariant implemented in ${relativePath}: ${pattern.description}`,
|
|
175
|
+
statement: `System invariant implemented in ${relativePath}: ${pattern.description}` || `Pattern-based invariant in ${relativePath}`, // Ensure statement exists
|
|
149
176
|
description: `System invariant implemented in ${relativePath}: ${pattern.description}`,
|
|
150
177
|
scope: { files: [relativePath] },
|
|
151
178
|
critical_path: true, // File specifically for invariants is likely critical
|
|
@@ -191,10 +218,13 @@ class InvariantDetector {
|
|
|
191
218
|
for (const pattern of validationPatterns) {
|
|
192
219
|
const matches = content.match(pattern);
|
|
193
220
|
if (matches) {
|
|
221
|
+
const matchCount = matches.length;
|
|
222
|
+
const patternName = pattern.source.replace(/\\/g, '').replace(/function\s+/, '');
|
|
194
223
|
invariants.push({
|
|
195
224
|
pattern: pattern.source,
|
|
196
|
-
description: `Validation function pattern found in ${filePath}: ${
|
|
197
|
-
isCritical: true
|
|
225
|
+
description: `Validation function pattern found in ${filePath}: ${matchCount} matches`,
|
|
226
|
+
isCritical: true,
|
|
227
|
+
position: content.indexOf(matches[0])
|
|
198
228
|
});
|
|
199
229
|
}
|
|
200
230
|
}
|
|
@@ -214,7 +244,8 @@ class InvariantDetector {
|
|
|
214
244
|
invariants.push({
|
|
215
245
|
pattern: pattern.source,
|
|
216
246
|
description: `Constraint enforcement pattern found in ${filePath}`,
|
|
217
|
-
isCritical: false
|
|
247
|
+
isCritical: false,
|
|
248
|
+
position: match.index
|
|
218
249
|
});
|
|
219
250
|
}
|
|
220
251
|
}
|
|
@@ -236,7 +267,8 @@ class InvariantDetector {
|
|
|
236
267
|
invariants.push({
|
|
237
268
|
pattern: pattern.source,
|
|
238
269
|
description: `State transition constraint pattern found in ${filePath}`,
|
|
239
|
-
isCritical: true
|
|
270
|
+
isCritical: true,
|
|
271
|
+
position: match.index
|
|
240
272
|
});
|
|
241
273
|
}
|
|
242
274
|
}
|
|
@@ -259,7 +291,8 @@ class InvariantDetector {
|
|
|
259
291
|
invariants.push({
|
|
260
292
|
pattern: pattern.source,
|
|
261
293
|
description: `Boundary enforcement pattern found in ${filePath}`,
|
|
262
|
-
isCritical: true
|
|
294
|
+
isCritical: true,
|
|
295
|
+
position: match.index
|
|
263
296
|
});
|
|
264
297
|
}
|
|
265
298
|
}
|
|
@@ -676,7 +709,7 @@ class InvariantDetector {
|
|
|
676
709
|
this.detectedInvariants.push({
|
|
677
710
|
id: this.generateInvariantId(`layer_${fromLayer}_not_depend_${toLayer}`, `${fromLayer}_${toLayer}`),
|
|
678
711
|
system: 'architectural',
|
|
679
|
-
statement: `Layer dependency rule: ${fromLayer} components should not depend on ${toLayer} components`,
|
|
712
|
+
statement: `Layer dependency rule: ${fromLayer} components should not depend on ${toLayer} components` || `Architectural layer constraint: ${fromLayer} -> ${toLayer}`, // Ensure statement exists
|
|
680
713
|
description: `Prevent ${fromLayer} layer from depending on ${toLayer} layer to maintain separation of concerns`,
|
|
681
714
|
scope: { files: dependencies.map(d => d.from) },
|
|
682
715
|
critical_path: true,
|
|
@@ -729,7 +762,7 @@ class InvariantDetector {
|
|
|
729
762
|
this.detectedInvariants.push({
|
|
730
763
|
id: this.generateInvariantId(`hub_protection_${nodeId}`, 'high_degree'),
|
|
731
764
|
system: 'architectural',
|
|
732
|
-
statement: `Protect high-degree node ${nodeId} from unauthorized modifications`,
|
|
765
|
+
statement: `Protect high-degree node ${nodeId} from unauthorized modifications` || `Hub node protection: ${nodeId}`, // Ensure statement exists
|
|
733
766
|
description: `Node ${nodeId} has ${degrees.incoming} incoming dependencies, changes here affect many components`,
|
|
734
767
|
scope: { files: [nodeId] },
|
|
735
768
|
critical_path: true,
|
|
@@ -781,7 +814,7 @@ class InvariantDetector {
|
|
|
781
814
|
this.detectedInvariants.push({
|
|
782
815
|
id: this.generateInvariantId(`tight_coupling_${nodeA}_${nodeB}`, 'bidirectional'),
|
|
783
816
|
system: 'architectural',
|
|
784
|
-
statement: `Prevent tight coupling between ${nodeA} and ${nodeB}`,
|
|
817
|
+
statement: `Prevent tight coupling between ${nodeA} and ${nodeB}` || `Coupling constraint: ${nodeA} and ${nodeB}`, // Ensure statement exists
|
|
785
818
|
description: `Bidirectional dependency detected between ${nodeA} and ${nodeB}, indicating tight coupling`,
|
|
786
819
|
scope: { files: [nodeA, nodeB] },
|
|
787
820
|
critical_path: false,
|
package/src/core/scanner.js
CHANGED
|
@@ -181,24 +181,69 @@ async function scan(directory) {
|
|
|
181
181
|
existingIds.add(existingInv.id);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
|
|
185
|
+
// Ensure all invariants have statement fields as fallback
|
|
186
|
+
console.log(`🔍 Ensuring all ${allInvariants.length} invariants have statement fields...`);
|
|
187
|
+
for (let i = 0; i < allInvariants.length; i++) {
|
|
188
|
+
const inv = allInvariants[i];
|
|
189
|
+
if (!inv.statement) {
|
|
190
|
+
// Create statement from available fields
|
|
191
|
+
if (inv.description) {
|
|
192
|
+
inv.statement = inv.description;
|
|
193
|
+
} else if (inv.id) {
|
|
194
|
+
inv.statement = `Invariant ${inv.id}`;
|
|
195
|
+
} else {
|
|
196
|
+
inv.statement = 'Unnamed architectural invariant';
|
|
197
|
+
}
|
|
198
|
+
console.log(`🔧 Added missing statement to invariant ${i + 1}/${allInvariants.length}: ${inv.id || 'unnamed'}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
console.log('✅ All invariants now have statement fields');
|
|
184
202
|
|
|
185
203
|
// Initialize components for new features
|
|
186
204
|
const ownershipResolver = new OwnershipResolver();
|
|
187
205
|
const failureModeSynthesizer = new FailureModeSynthesizer();
|
|
188
206
|
|
|
189
207
|
// Resolve ownership
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
208
|
+
let ownership = {};
|
|
209
|
+
try {
|
|
210
|
+
console.log('🔍 Resolving ownership...');
|
|
211
|
+
ownership = ownershipResolver.resolveOwnership(
|
|
212
|
+
{ nodes, edges }
|
|
213
|
+
);
|
|
214
|
+
console.log('✅ Ownership resolved');
|
|
215
|
+
} catch (ownershipError) {
|
|
216
|
+
console.warn(`⚠️ Failed to resolve ownership: ${ownershipError.message}`);
|
|
217
|
+
console.warn('Continuing with empty ownership data...');
|
|
218
|
+
ownership = {};
|
|
219
|
+
}
|
|
193
220
|
|
|
194
221
|
// Synthesize failure modes
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
allInvariants
|
|
198
|
-
|
|
222
|
+
let failure_modes = [];
|
|
223
|
+
try {
|
|
224
|
+
console.log('🔍 Synthesizing failure modes for ${allInvariants.length} invariants...');
|
|
225
|
+
failure_modes = failureModeSynthesizer.synthesizeFailureModes(
|
|
226
|
+
{ nodes, edges },
|
|
227
|
+
allInvariants
|
|
228
|
+
);
|
|
229
|
+
console.log(`✅ Generated ${failure_modes.length} failure modes`);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.warn(`⚠️ Failed to synthesize failure modes: ${error.message}`);
|
|
232
|
+
console.warn('Continuing with empty failure modes...');
|
|
233
|
+
failure_modes = [];
|
|
234
|
+
}
|
|
199
235
|
|
|
200
236
|
// Generate decision guidance
|
|
201
|
-
|
|
237
|
+
let decision_guidance = {};
|
|
238
|
+
try {
|
|
239
|
+
console.log('🔍 Generating decision guidance...');
|
|
240
|
+
decision_guidance = generateDecisionGuidance(allInvariants, failure_modes);
|
|
241
|
+
console.log('✅ Decision guidance generated');
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.warn(`⚠️ Failed to generate decision guidance: ${error.message}`);
|
|
244
|
+
console.warn('Continuing with empty decision guidance...');
|
|
245
|
+
decision_guidance = {};
|
|
246
|
+
}
|
|
202
247
|
|
|
203
248
|
console.log(`📊 Total invariants to "Set" in context: ${allInvariants.length}`);
|
|
204
249
|
|
|
@@ -209,12 +254,18 @@ async function scan(directory) {
|
|
|
209
254
|
if (allInvariants && allInvariants.length > 0) {
|
|
210
255
|
// Analyze all invariants (detected + existing)
|
|
211
256
|
const invariantAnalyzer = new InvariantAnalyzer();
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
257
|
+
try {
|
|
258
|
+
invariantAnalysis = invariantAnalyzer.analyzeInvariants(allInvariants, {
|
|
259
|
+
directory,
|
|
260
|
+
projectName: path.basename(directory),
|
|
261
|
+
nodeCount: nodes.length,
|
|
262
|
+
edgeCount: edges.length
|
|
263
|
+
});
|
|
264
|
+
} catch (analysisError) {
|
|
265
|
+
console.warn(`⚠️ Failed to analyze invariants: ${analysisError.message}`);
|
|
266
|
+
console.warn('Continuing with empty invariant analysis...');
|
|
267
|
+
invariantAnalysis = null;
|
|
268
|
+
}
|
|
218
269
|
|
|
219
270
|
// Assess architectural health based on all invariants
|
|
220
271
|
try {
|
|
@@ -234,17 +285,31 @@ async function scan(directory) {
|
|
|
234
285
|
} else {
|
|
235
286
|
// Even if no invariants are detected, create a baseline analysis
|
|
236
287
|
const invariantAnalyzer = new InvariantAnalyzer();
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
288
|
+
try {
|
|
289
|
+
invariantAnalysis = invariantAnalyzer.analyzeInvariants([], {
|
|
290
|
+
directory,
|
|
291
|
+
projectName: path.basename(directory),
|
|
292
|
+
nodeCount: nodes.length,
|
|
293
|
+
edgeCount: edges.length
|
|
294
|
+
});
|
|
295
|
+
} catch (baselineError) {
|
|
296
|
+
console.warn(`⚠️ Failed to create baseline invariant analysis: ${baselineError.message}`);
|
|
297
|
+
invariantAnalysis = null;
|
|
298
|
+
}
|
|
243
299
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
300
|
+
try {
|
|
301
|
+
architecturalHealth = invariantAnalyzer.assessArchitecturalHealth([], {
|
|
302
|
+
directory,
|
|
303
|
+
projectName: path.basename(directory)
|
|
304
|
+
});
|
|
305
|
+
} catch (baselineHealthError) {
|
|
306
|
+
console.warn(`⚠️ Failed to assess baseline architectural health: ${baselineHealthError.message}`);
|
|
307
|
+
architecturalHealth = {
|
|
308
|
+
score: 0,
|
|
309
|
+
level: 'unknown',
|
|
310
|
+
assessment: 'Could not assess architectural health due to analysis error'
|
|
311
|
+
};
|
|
312
|
+
}
|
|
248
313
|
}
|
|
249
314
|
|
|
250
315
|
// --- FINAL BUILD & VALIDATION ---
|
|
@@ -343,8 +408,32 @@ async function scan(directory) {
|
|
|
343
408
|
|
|
344
409
|
// Ensure authority ledger directory exists
|
|
345
410
|
const arcvisionDir = path.join(directory, 'arcvision_context');
|
|
411
|
+
console.log(`🔍 Ensuring arcvision_context directory exists at: ${arcvisionDir}`);
|
|
346
412
|
if (!fs.existsSync(arcvisionDir)) {
|
|
413
|
+
console.log('📁 Creating arcvision_context directory...');
|
|
347
414
|
fs.mkdirSync(arcvisionDir, { recursive: true });
|
|
415
|
+
console.log('✅ arcvision_context directory created');
|
|
416
|
+
} else {
|
|
417
|
+
console.log('✅ arcvision_context directory already exists');
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Write context to file
|
|
421
|
+
const contextFilePath = path.join(arcvisionDir, 'arcvision.context.json');
|
|
422
|
+
console.log(`💾 Writing context to: ${contextFilePath}`);
|
|
423
|
+
try {
|
|
424
|
+
fs.writeFileSync(contextFilePath, JSON.stringify(sortedContext, null, 2));
|
|
425
|
+
console.log('✅ Context file written successfully');
|
|
426
|
+
|
|
427
|
+
// Verify file was written
|
|
428
|
+
if (fs.existsSync(contextFilePath)) {
|
|
429
|
+
const stats = fs.statSync(contextFilePath);
|
|
430
|
+
console.log(`📊 Context file size: ${stats.size} bytes`);
|
|
431
|
+
} else {
|
|
432
|
+
console.warn('⚠️ Context file was not found after write operation');
|
|
433
|
+
}
|
|
434
|
+
} catch (writeError) {
|
|
435
|
+
console.error(`❌ Failed to write context file: ${writeError.message}`);
|
|
436
|
+
throw writeError;
|
|
348
437
|
}
|
|
349
438
|
|
|
350
439
|
console.log(' ✅ Context "Set" & Verified');
|