aios-core 4.2.13 → 4.2.15

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 (95) hide show
  1. package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
  4. package/.aios-core/data/entity-registry.yaml +27 -0
  5. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  6. package/.aios-core/development/scripts/backup-manager.js +606 -606
  7. package/.aios-core/development/scripts/branch-manager.js +389 -389
  8. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  9. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  10. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  11. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  12. package/.aios-core/development/scripts/diff-generator.js +351 -351
  13. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  14. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  15. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  16. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  17. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  18. package/.aios-core/development/scripts/modification-validator.js +554 -554
  19. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  20. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  21. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  22. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  23. package/.aios-core/development/scripts/security-checker.js +358 -358
  24. package/.aios-core/development/scripts/template-engine.js +239 -239
  25. package/.aios-core/development/scripts/template-validator.js +278 -278
  26. package/.aios-core/development/scripts/test-generator.js +843 -843
  27. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  28. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  29. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  30. package/.aios-core/development/scripts/version-tracker.js +526 -526
  31. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  32. package/.aios-core/development/tasks/build-autonomous.md +10 -4
  33. package/.aios-core/development/tasks/create-service.md +23 -0
  34. package/.aios-core/development/tasks/dev-develop-story.md +12 -6
  35. package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
  36. package/.aios-core/development/tasks/publish-npm.md +3 -3
  37. package/.aios-core/hooks/unified/README.md +1 -1
  38. package/.aios-core/install-manifest.yaml +65 -61
  39. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  42. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  43. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  44. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  46. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  47. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  48. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  49. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  50. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  51. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  52. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  53. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  54. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  55. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  56. package/README.en.md +747 -0
  57. package/README.md +4 -2
  58. package/bin/aios.js +7 -4
  59. package/package.json +1 -1
  60. package/packages/aios-pro-cli/src/recover.js +1 -1
  61. package/packages/installer/src/wizard/ide-config-generator.js +6 -6
  62. package/packages/installer/src/wizard/pro-setup.js +3 -3
  63. package/pro/license/degradation.js +220 -220
  64. package/pro/license/errors.js +450 -450
  65. package/pro/license/feature-gate.js +354 -354
  66. package/pro/license/index.js +181 -181
  67. package/pro/license/license-cache.js +523 -523
  68. package/pro/license/license-crypto.js +303 -303
  69. package/scripts/package-synapse.js +5 -5
  70. package/scripts/validate-package-completeness.js +3 -3
  71. package/.aios-core/.session/current-session.json +0 -14
  72. package/.aios-core/data/registry-update-log.jsonl +0 -191
  73. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
  74. package/.aios-core/docs/component-creation-guide.md +0 -458
  75. package/.aios-core/docs/session-update-pattern.md +0 -307
  76. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
  77. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
  78. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
  79. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
  80. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
  81. package/.aios-core/docs/template-syntax.md +0 -267
  82. package/.aios-core/docs/troubleshooting-guide.md +0 -625
  83. package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
  84. package/.aios-core/manifests/agents.csv +0 -29
  85. package/.aios-core/manifests/tasks.csv +0 -198
  86. package/.aios-core/manifests/workers.csv +0 -204
  87. package/.claude/rules/agent-authority.md +0 -105
  88. package/.claude/rules/coderabbit-integration.md +0 -93
  89. package/.claude/rules/ids-principles.md +0 -112
  90. package/.claude/rules/story-lifecycle.md +0 -139
  91. package/.claude/rules/workflow-execution.md +0 -150
  92. package/scripts/glue/README.md +0 -355
  93. package/scripts/glue/compose-agent-prompt.cjs +0 -362
  94. /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
  95. /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
@@ -1,643 +1,643 @@
1
- const fs = require('fs').promises;
2
- const path = require('path');
3
- const chalk = require('chalk');
4
- const inquirer = require('inquirer');
5
-
6
- /**
7
- * Approval workflow for AIOS-FULLSTACK framework
8
- * Manages approval process for high-impact modifications
9
- */
10
- class ApprovalWorkflow {
11
- constructor(options = {}) {
12
- this.rootPath = options.rootPath || process.cwd();
13
- this.approvalThresholds = {
14
- low: { auto_approve: true, requires_review: false },
15
- medium: { auto_approve: false, requires_review: true },
16
- high: { auto_approve: false, requires_review: true, requires_approval: true },
17
- critical: { auto_approve: false, requires_review: true, requires_approval: true, requires_multiple_approvers: true }
18
- };
19
- this.approvalHistory = [];
20
- this.pendingApprovals = new Map();
21
- this.approvalRules = new Map();
22
- this.initializeApprovalRules();
23
- }
24
-
25
- /**
26
- * Initialize default approval rules
27
- */
28
- initializeApprovalRules() {
29
- // Component type rules
30
- this.approvalRules.set('agent_modification', {
31
- risk_threshold: 'medium',
32
- required_approvers: 1,
33
- timeout_hours: 24,
34
- auto_approve_conditions: ['low_risk', 'has_tests', 'non_breaking']
35
- });
36
-
37
- this.approvalRules.set('workflow_modification', {
38
- risk_threshold: 'medium',
39
- required_approvers: 1,
40
- timeout_hours: 48,
41
- auto_approve_conditions: ['low_risk', 'has_tests']
42
- });
43
-
44
- this.approvalRules.set('core_util_modification', {
45
- risk_threshold: 'low',
46
- required_approvers: 2,
47
- timeout_hours: 72,
48
- auto_approve_conditions: ['minimal_risk', 'comprehensive_tests']
49
- });
50
-
51
- // Modification type rules
52
- this.approvalRules.set('component_removal', {
53
- risk_threshold: 'low',
54
- required_approvers: 2,
55
- timeout_hours: 168, // 1 week
56
- auto_approve_conditions: [] // Never auto-approve removals
57
- });
58
-
59
- this.approvalRules.set('breaking_change', {
60
- risk_threshold: 'low',
61
- required_approvers: 2,
62
- timeout_hours: 96,
63
- auto_approve_conditions: []
64
- });
65
- }
66
-
67
- /**
68
- * Process approval request for impact report
69
- */
70
- async processApprovalRequest(impactReport, options = {}) {
71
- const requestId = `approval-${Date.now()}`;
72
-
73
- try {
74
- console.log(chalk.blue(`🔍 Processing approval request for: ${impactReport.targetComponent.path}`));
75
-
76
- const _config = {
77
- skip_approval: options.skip_approval || false,
78
- auto_approve_low_risk: options.auto_approve_low_risk !== false,
79
- timeout_hours: options.timeout_hours || 24,
80
- required_approvers: options.required_approvers,
81
- ...options
82
- };
83
-
84
- // Determine approval requirements
85
- const approvalRequirements = await this.determineApprovalRequirements(impactReport, config);
86
-
87
- // Check if modification can be auto-approved
88
- const autoApprovalResult = await this.checkAutoApproval(impactReport, approvalRequirements);
89
-
90
- if (autoApprovalResult.can_auto_approve) {
91
- const approvalResult = await this.executeAutoApproval(impactReport, autoApprovalResult, requestId);
92
- return approvalResult;
93
- }
94
-
95
- // Manual approval required
96
- const approvalResult = await this.executeManualApproval(
97
- impactReport,
98
- approvalRequirements,
99
- config,
100
- requestId
101
- );
102
-
103
- return approvalResult;
104
-
105
- } catch (_error) {
106
- console.error(chalk.red(`Approval process failed: ${error.message}`));
107
- throw error;
108
- }
109
- }
110
-
111
- /**
112
- * Determine approval requirements based on impact analysis
113
- */
114
- async determineApprovalRequirements(impactReport, config) {
115
- const requirements = {
116
- approval_needed: false,
117
- risk_level: impactReport.riskAssessment.overallRisk,
118
- risk_score: impactReport.riskAssessment.riskScore,
119
- required_approvers: 1,
120
- timeout_hours: 24,
121
- review_criteria: [],
122
- blocking_issues: []
123
- };
124
-
125
- const riskLevel = impactReport.riskAssessment.overallRisk;
126
- const thresholds = this.approvalThresholds[riskLevel];
127
-
128
- // Basic approval requirements from risk level
129
- if (thresholds) {
130
- requirements.approval_needed = !thresholds.auto_approve;
131
- requirements.requires_review = thresholds.requires_review;
132
- requirements.requires_approval = thresholds.requires_approval;
133
- requirements.requires_multiple_approvers = thresholds.requires_multiple_approvers;
134
- }
135
-
136
- // Component-specific rules
137
- const componentRule = this.getComponentApprovalRule(impactReport.targetComponent);
138
- if (componentRule) {
139
- requirements.required_approvers = Math.max(requirements.required_approvers, componentRule.required_approvers);
140
- requirements.timeout_hours = Math.max(requirements.timeout_hours, componentRule.timeout_hours);
141
- }
142
-
143
- // Modification-specific rules
144
- const modificationRule = this.getModificationApprovalRule(impactReport.modificationType);
145
- if (modificationRule) {
146
- requirements.required_approvers = Math.max(requirements.required_approvers, modificationRule.required_approvers);
147
- requirements.timeout_hours = Math.max(requirements.timeout_hours, modificationRule.timeout_hours);
148
- }
149
-
150
- // Critical issues that block auto-approval
151
- if (impactReport.riskAssessment.criticalIssues.length > 0) {
152
- requirements.approval_needed = true;
153
- requirements.blocking_issues = impactReport.riskAssessment.criticalIssues.map(issue => issue.description);
154
- }
155
-
156
- // High-impact propagation
157
- if (impactReport.propagationAnalysis.criticalPaths?.length > 2) {
158
- requirements.approval_needed = true;
159
- requirements.review_criteria.push('Multiple critical propagation paths require review');
160
- }
161
-
162
- // Many affected components
163
- if (impactReport.summary.affectedComponents > 20) {
164
- requirements.approval_needed = true;
165
- requirements.review_criteria.push('Large number of affected components requires careful review');
166
- }
167
-
168
- // Breaking changes
169
- const hasBreakingChanges = impactReport.propagationAnalysis.directEffects?.some(
170
- effect => effect.changeType?.severity === 'breaking'
171
- ) || false;
172
-
173
- if (hasBreakingChanges) {
174
- requirements.approval_needed = true;
175
- requirements.required_approvers = Math.max(requirements.required_approvers, 2);
176
- requirements.review_criteria.push('Breaking changes require multiple approvers');
177
- }
178
-
179
- // Security-sensitive modifications
180
- if (impactReport.riskAssessment.riskDimensions?.security_risk?.score >= 6) {
181
- requirements.approval_needed = true;
182
- requirements.review_criteria.push('Security-sensitive modification requires approval');
183
- }
184
-
185
- return requirements;
186
- }
187
-
188
- /**
189
- * Check if modification can be auto-approved
190
- */
191
- async checkAutoApproval(impactReport, requirements) {
192
- const result = {
193
- can_auto_approve: false,
194
- reasons: [],
195
- conditions_met: [],
196
- conditions_failed: []
197
- };
198
-
199
- // Never auto-approve if manual approval is explicitly needed
200
- if (requirements.approval_needed) {
201
- result.reasons.push('Manual approval explicitly required due to risk level or critical issues');
202
- return result;
203
- }
204
-
205
- // Never auto-approve critical risk modifications
206
- if (impactReport.riskAssessment.overallRisk === 'critical') {
207
- result.reasons.push('Critical risk level requires manual approval');
208
- return result;
209
- }
210
-
211
- // Never auto-approve component removals
212
- if (impactReport.modificationType === 'remove') {
213
- result.reasons.push('Component removal always requires manual approval');
214
- return result;
215
- }
216
-
217
- // Check auto-approval conditions
218
- const autoApprovalConditions = await this.evaluateAutoApprovalConditions(impactReport);
219
-
220
- if (autoApprovalConditions.all_conditions_met) {
221
- result.can_auto_approve = true;
222
- result.conditions_met = autoApprovalConditions.met_conditions;
223
- result.reasons.push('All auto-approval conditions satisfied');
224
- } else {
225
- result.conditions_failed = autoApprovalConditions.failed_conditions;
226
- result.reasons.push('Auto-approval conditions not met');
227
- }
228
-
229
- return result;
230
- }
231
-
232
- /**
233
- * Evaluate auto-approval conditions
234
- */
235
- async evaluateAutoApprovalConditions(impactReport) {
236
- const conditions = {
237
- low_risk: impactReport.riskAssessment.overallRisk === 'low',
238
- minimal_impact: impactReport.summary.affectedComponents <= 5,
239
- no_critical_issues: impactReport.riskAssessment.criticalIssues.length === 0,
240
- no_breaking_changes: !this.hasBreakingChanges(impactReport),
241
- has_tests: await this.componentHasTests(impactReport.targetComponent),
242
- small_change: this.isSmallChange(impactReport),
243
- no_security_risk: impactReport.riskAssessment.riskDimensions?.security_risk?.score < 5
244
- };
245
-
246
- const metConditions = Object.entries(conditions)
247
- .filter(([condition, met]) => met)
248
- .map(([condition]) => condition);
249
-
250
- const failedConditions = Object.entries(conditions)
251
- .filter(([condition, met]) => !met)
252
- .map(([condition]) => condition);
253
-
254
- // Require at least 5 out of 7 conditions for auto-approval
255
- const requiredConditions = 5;
256
- const allConditionsMet = metConditions.length >= requiredConditions;
257
-
258
- return {
259
- all_conditions_met: allConditionsMet,
260
- met_conditions: metConditions,
261
- failed_conditions: failedConditions,
262
- condition_score: `${metConditions.length}/${Object.keys(conditions).length}`
263
- };
264
- }
265
-
266
- /**
267
- * Execute auto-approval
268
- */
269
- async executeAutoApproval(impactReport, autoApprovalResult, requestId) {
270
- const approval = {
271
- request_id: requestId,
272
- target_component: impactReport.targetComponent.path,
273
- modification_type: impactReport.modificationType,
274
- approval_status: 'auto_approved',
275
- approval_type: 'automatic',
276
- risk_level: impactReport.riskAssessment.overallRisk,
277
- auto_approval_reasons: autoApprovalResult.reasons,
278
- conditions_met: autoApprovalResult.conditions_met,
279
- approved_by: 'system_auto_approval',
280
- approved_at: new Date().toISOString(),
281
- valid_until: this.calculateExpirationTime(24), // Auto-approvals valid for 24 hours
282
- metadata: {
283
- impact_summary: impactReport.summary,
284
- approval_confidence: this.calculateApprovalConfidence(autoApprovalResult)
285
- }
286
- };
287
-
288
- // Log approval
289
- await this.logApproval(approval);
290
-
291
- console.log(chalk.green(`✅ Auto-approved: ${impactReport.targetComponent.path}`));
292
- console.log(chalk.gray(` Risk level: ${impactReport.riskAssessment.overallRisk}`));
293
- console.log(chalk.gray(` Conditions met: ${autoApprovalResult.conditions_met.length}`));
294
-
295
- return approval;
296
- }
297
-
298
- /**
299
- * Execute manual approval process
300
- */
301
- async executeManualApproval(impactReport, requirements, config, requestId) {
302
- console.log(chalk.yellow(`\n⚠️ MANUAL APPROVAL REQUIRED`));
303
- console.log(chalk.gray(`Component: ${impactReport.targetComponent.path}`));
304
- console.log(chalk.gray(`Risk Level: ${impactReport.riskAssessment.overallRisk.toUpperCase()}`));
305
- console.log(chalk.gray(`Affected Components: ${impactReport.summary.affectedComponents}`));
306
-
307
- if (requirements.blocking_issues.length > 0) {
308
- console.log(chalk.red(`\nBlocking Issues:`));
309
- requirements.blocking_issues.forEach((issue, index) => {
310
- console.log(chalk.red(` ${index + 1}. ${issue}`));
311
- });
312
- }
313
-
314
- if (requirements.review_criteria.length > 0) {
315
- console.log(chalk.yellow(`\nReview Criteria:`));
316
- requirements.review_criteria.forEach((criteria, index) => {
317
- console.log(chalk.yellow(` ${index + 1}. ${criteria}`));
318
- });
319
- }
320
-
321
- // Display key recommendations
322
- if (impactReport.riskAssessment.recommendations.length > 0) {
323
- console.log(chalk.blue(`\nKey Recommendations:`));
324
- impactReport.riskAssessment.recommendations.slice(0, 3).forEach((rec, index) => {
325
- console.log(chalk.blue(` ${index + 1}. ${rec.title}`));
326
- console.log(chalk.gray(` ${rec.description}`));
327
- });
328
- }
329
-
330
- // Approval prompt
331
- const approvalQuestions = await this.buildApprovalQuestions(impactReport, requirements);
332
- const approvalAnswers = await inquirer.prompt(approvalQuestions);
333
-
334
- const approval = {
335
- request_id: requestId,
336
- target_component: impactReport.targetComponent.path,
337
- modification_type: impactReport.modificationType,
338
- approval_status: approvalAnswers.approved ? 'approved' : 'rejected',
339
- approval_type: 'manual',
340
- risk_level: impactReport.riskAssessment.overallRisk,
341
- approved_by: approvalAnswers.approver_name || 'user',
342
- approved_at: new Date().toISOString(),
343
- approval_reason: approvalAnswers.approval_reason,
344
- conditions_acknowledged: approvalAnswers.conditions_acknowledged || false,
345
- valid_until: this.calculateExpirationTime(requirements.timeout_hours),
346
- requirements_met: requirements,
347
- metadata: {
348
- impact_summary: impactReport.summary,
349
- approval_answers: approvalAnswers
350
- }
351
- };
352
-
353
- // Add approval conditions if approved
354
- if (approvalAnswers.approved) {
355
- approval.approval_conditions = approvalAnswers.approval_conditions;
356
- approval.monitoring_required = approvalAnswers.monitoring_required || false;
357
- approval.rollback_plan_required = approvalAnswers.rollback_plan || false;
358
- } else {
359
- approval.rejection_reason = approvalAnswers.rejection_reason;
360
- approval.recommended_actions = approvalAnswers.recommended_actions;
361
- }
362
-
363
- // Log approval decision
364
- await this.logApproval(approval);
365
-
366
- if (approvalAnswers.approved) {
367
- console.log(chalk.green(`\n✅ Manual approval granted`));
368
- console.log(chalk.gray(` Approved by: ${approval.approved_by}`));
369
- console.log(chalk.gray(` Valid until: ${new Date(approval.valid_until).toLocaleString()}`));
370
-
371
- if (approval.approval_conditions) {
372
- console.log(chalk.blue(` Conditions: ${approval.approval_conditions}`));
373
- }
374
- } else {
375
- console.log(chalk.red(`\n❌ Approval rejected`));
376
- console.log(chalk.gray(` Reason: ${approval.rejection_reason}`));
377
- }
378
-
379
- return approval;
380
- }
381
-
382
- /**
383
- * Build approval question flow
384
- */
385
- async buildApprovalQuestions(impactReport, requirements) {
386
- const questions = [];
387
-
388
- // Main approval question
389
- questions.push({
390
- type: 'confirm',
391
- name: 'approved',
392
- message: `Approve ${impactReport.modificationType} of ${impactReport.targetComponent.path}?`,
393
- default: false
394
- });
395
-
396
- // Conditional questions based on approval
397
- questions.push({
398
- type: 'input',
399
- name: 'approver_name',
400
- message: 'Enter your name/identifier:',
401
- when: (answers) => answers.approved,
402
- validate: (input) => input.length > 0 || 'Name is required'
403
- });
404
-
405
- questions.push({
406
- type: 'input',
407
- name: 'approval_reason',
408
- message: 'Reason for approval:',
409
- when: (answers) => answers.approved,
410
- default: 'Impact analysis reviewed and acceptable'
411
- });
412
-
413
- // High-risk additional questions
414
- if (requirements.risk_level === 'high' || requirements.risk_level === 'critical') {
415
- questions.push({
416
- type: 'confirm',
417
- name: 'conditions_acknowledged',
418
- message: 'Do you acknowledge all risk factors and recommendations?',
419
- when: (answers) => answers.approved,
420
- default: false
421
- });
422
-
423
- questions.push({
424
- type: 'input',
425
- name: 'approval_conditions',
426
- message: 'Enter any approval conditions or requirements:',
427
- when: (answers) => answers.approved && answers.conditions_acknowledged,
428
- default: 'Standard monitoring and rollback procedures apply'
429
- });
430
-
431
- questions.push({
432
- type: 'confirm',
433
- name: 'monitoring_required',
434
- message: 'Require enhanced monitoring after deployment?',
435
- when: (answers) => answers.approved,
436
- default: true
437
- });
438
-
439
- questions.push({
440
- type: 'confirm',
441
- name: 'rollback_plan',
442
- message: 'Require documented rollback plan?',
443
- when: (answers) => answers.approved,
444
- default: true
445
- });
446
- }
447
-
448
- // Rejection questions
449
- questions.push({
450
- type: 'input',
451
- name: 'rejection_reason',
452
- message: 'Reason for rejection:',
453
- when: (answers) => !answers.approved,
454
- validate: (input) => input.length > 0 || 'Rejection reason is required'
455
- });
456
-
457
- questions.push({
458
- type: 'input',
459
- name: 'recommended_actions',
460
- message: 'Recommended actions before resubmission:',
461
- when: (answers) => !answers.approved,
462
- default: 'Address critical issues and reduce risk factors'
463
- });
464
-
465
- return questions;
466
- }
467
-
468
- /**
469
- * Log approval decision for audit trail
470
- */
471
- async logApproval(approval) {
472
- // Add to approval history
473
- this.approvalHistory.push({
474
- request_id: approval.request_id,
475
- component: approval.target_component,
476
- status: approval.approval_status,
477
- risk_level: approval.risk_level,
478
- approved_by: approval.approved_by,
479
- timestamp: approval.approved_at
480
- });
481
-
482
- // Write to audit log file
483
- try {
484
- const logDir = path.join(this.rootPath, '.aios', 'audit');
485
- await fs.mkdir(logDir, { recursive: true });
486
-
487
- const logFile = path.join(logDir, 'approval_log.jsonl');
488
- const logEntry = JSON.stringify(approval) + '\n';
489
-
490
- await fs.appendFile(logFile, logEntry);
491
-
492
- console.log(chalk.gray(` Approval logged to audit trail`));
493
-
494
- } catch (_error) {
495
- console.warn(chalk.yellow(`Failed to write approval log: ${error.message}`));
496
- }
497
- }
498
-
499
- // Helper methods
500
-
501
- getComponentApprovalRule(component) {
502
- if (component.type === 'agent') {
503
- return this.approvalRules.get('agent_modification');
504
- } else if (component.type === 'workflow') {
505
- return this.approvalRules.get('workflow_modification');
506
- } else if (component.type === 'util' && component.path.includes('core')) {
507
- return this.approvalRules.get('core_util_modification');
508
- }
509
- return null;
510
- }
511
-
512
- getModificationApprovalRule(modificationType) {
513
- if (modificationType === 'remove') {
514
- return this.approvalRules.get('component_removal');
515
- }
516
- return null;
517
- }
518
-
519
- hasBreakingChanges(impactReport) {
520
- return impactReport.propagationAnalysis.directEffects?.some(
521
- effect => effect.changeType?.severity === 'breaking'
522
- ) || false;
523
- }
524
-
525
- async componentHasTests(component) {
526
- const testPaths = [
527
- path.join(this.rootPath, 'tests', 'unit', component.type, `${component.name}.test.js`),
528
- path.join(this.rootPath, 'tests', 'integration', component.type, `${component.name}.integration.test.js`),
529
- path.join(this.rootPath, 'test', `${component.name}.test.js`)
530
- ];
531
-
532
- for (const testPath of testPaths) {
533
- try {
534
- await fs.access(testPath);
535
- return true;
536
- } catch (_error) {
537
- // File doesn't exist, continue
538
- }
539
- }
540
-
541
- return false;
542
- }
543
-
544
- isSmallChange(impactReport) {
545
- return impactReport.summary.affectedComponents <= 3 &&
546
- impactReport.summary.propagationDepth <= 2;
547
- }
548
-
549
- calculateExpirationTime(hours) {
550
- const now = new Date();
551
- now.setHours(now.getHours() + hours);
552
- return now.toISOString();
553
- }
554
-
555
- calculateApprovalConfidence(autoApprovalResult) {
556
- const conditionsMet = autoApprovalResult.conditions_met.length;
557
- const totalConditions = 7; // Based on evaluateAutoApprovalConditions
558
- return Math.round((conditionsMet / totalConditions) * 100);
559
- }
560
-
561
- /**
562
- * Check if approval is still valid
563
- */
564
- isApprovalValid(approval) {
565
- const now = new Date();
566
- const validUntil = new Date(approval.valid_until);
567
- return now < validUntil;
568
- }
569
-
570
- /**
571
- * Get approval history
572
- */
573
- getApprovalHistory(options = {}) {
574
- const history = {
575
- total_approvals: this.approvalHistory.length,
576
- approval_stats: this.calculateApprovalStats(),
577
- recent_approvals: this.approvalHistory.slice(-10)
578
- };
579
-
580
- if (options.component) {
581
- history.component_approvals = this.approvalHistory.filter(
582
- approval => approval.component === options.component
583
- );
584
- }
585
-
586
- if (options.risk_level) {
587
- history.risk_level_approvals = this.approvalHistory.filter(
588
- approval => approval.risk_level === options.risk_level
589
- );
590
- }
591
-
592
- return history;
593
- }
594
-
595
- calculateApprovalStats() {
596
- const stats = {
597
- approved: 0,
598
- rejected: 0,
599
- auto_approved: 0,
600
- by_risk_level: { low: 0, medium: 0, high: 0, critical: 0 }
601
- };
602
-
603
- this.approvalHistory.forEach(approval => {
604
- if (approval.status === 'approved') stats.approved++;
605
- else if (approval.status === 'rejected') stats.rejected++;
606
- else if (approval.status === 'auto_approved') stats.auto_approved++;
607
-
608
- stats.by_risk_level[approval.risk_level]++;
609
- });
610
-
611
- return stats;
612
- }
613
-
614
- /**
615
- * Get pending approvals
616
- */
617
- getPendingApprovals() {
618
- return Array.from(this.pendingApprovals.values());
619
- }
620
-
621
- /**
622
- * Clear expired approvals
623
- */
624
- clearExpiredApprovals() {
625
- const now = new Date();
626
- let clearedCount = 0;
627
-
628
- for (const [requestId, approval] of this.pendingApprovals) {
629
- if (new Date(approval.valid_until) < now) {
630
- this.pendingApprovals.delete(requestId);
631
- clearedCount++;
632
- }
633
- }
634
-
635
- if (clearedCount > 0) {
636
- console.log(chalk.gray(`Cleared ${clearedCount} expired approvals`));
637
- }
638
-
639
- return clearedCount;
640
- }
641
- }
642
-
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const inquirer = require('inquirer');
5
+
6
+ /**
7
+ * Approval workflow for AIOS-FULLSTACK framework
8
+ * Manages approval process for high-impact modifications
9
+ */
10
+ class ApprovalWorkflow {
11
+ constructor(options = {}) {
12
+ this.rootPath = options.rootPath || process.cwd();
13
+ this.approvalThresholds = {
14
+ low: { auto_approve: true, requires_review: false },
15
+ medium: { auto_approve: false, requires_review: true },
16
+ high: { auto_approve: false, requires_review: true, requires_approval: true },
17
+ critical: { auto_approve: false, requires_review: true, requires_approval: true, requires_multiple_approvers: true }
18
+ };
19
+ this.approvalHistory = [];
20
+ this.pendingApprovals = new Map();
21
+ this.approvalRules = new Map();
22
+ this.initializeApprovalRules();
23
+ }
24
+
25
+ /**
26
+ * Initialize default approval rules
27
+ */
28
+ initializeApprovalRules() {
29
+ // Component type rules
30
+ this.approvalRules.set('agent_modification', {
31
+ risk_threshold: 'medium',
32
+ required_approvers: 1,
33
+ timeout_hours: 24,
34
+ auto_approve_conditions: ['low_risk', 'has_tests', 'non_breaking']
35
+ });
36
+
37
+ this.approvalRules.set('workflow_modification', {
38
+ risk_threshold: 'medium',
39
+ required_approvers: 1,
40
+ timeout_hours: 48,
41
+ auto_approve_conditions: ['low_risk', 'has_tests']
42
+ });
43
+
44
+ this.approvalRules.set('core_util_modification', {
45
+ risk_threshold: 'low',
46
+ required_approvers: 2,
47
+ timeout_hours: 72,
48
+ auto_approve_conditions: ['minimal_risk', 'comprehensive_tests']
49
+ });
50
+
51
+ // Modification type rules
52
+ this.approvalRules.set('component_removal', {
53
+ risk_threshold: 'low',
54
+ required_approvers: 2,
55
+ timeout_hours: 168, // 1 week
56
+ auto_approve_conditions: [] // Never auto-approve removals
57
+ });
58
+
59
+ this.approvalRules.set('breaking_change', {
60
+ risk_threshold: 'low',
61
+ required_approvers: 2,
62
+ timeout_hours: 96,
63
+ auto_approve_conditions: []
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Process approval request for impact report
69
+ */
70
+ async processApprovalRequest(impactReport, options = {}) {
71
+ const requestId = `approval-${Date.now()}`;
72
+
73
+ try {
74
+ console.log(chalk.blue(`🔍 Processing approval request for: ${impactReport.targetComponent.path}`));
75
+
76
+ const _config = {
77
+ skip_approval: options.skip_approval || false,
78
+ auto_approve_low_risk: options.auto_approve_low_risk !== false,
79
+ timeout_hours: options.timeout_hours || 24,
80
+ required_approvers: options.required_approvers,
81
+ ...options
82
+ };
83
+
84
+ // Determine approval requirements
85
+ const approvalRequirements = await this.determineApprovalRequirements(impactReport, config);
86
+
87
+ // Check if modification can be auto-approved
88
+ const autoApprovalResult = await this.checkAutoApproval(impactReport, approvalRequirements);
89
+
90
+ if (autoApprovalResult.can_auto_approve) {
91
+ const approvalResult = await this.executeAutoApproval(impactReport, autoApprovalResult, requestId);
92
+ return approvalResult;
93
+ }
94
+
95
+ // Manual approval required
96
+ const approvalResult = await this.executeManualApproval(
97
+ impactReport,
98
+ approvalRequirements,
99
+ config,
100
+ requestId
101
+ );
102
+
103
+ return approvalResult;
104
+
105
+ } catch (_error) {
106
+ console.error(chalk.red(`Approval process failed: ${error.message}`));
107
+ throw error;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Determine approval requirements based on impact analysis
113
+ */
114
+ async determineApprovalRequirements(impactReport, config) {
115
+ const requirements = {
116
+ approval_needed: false,
117
+ risk_level: impactReport.riskAssessment.overallRisk,
118
+ risk_score: impactReport.riskAssessment.riskScore,
119
+ required_approvers: 1,
120
+ timeout_hours: 24,
121
+ review_criteria: [],
122
+ blocking_issues: []
123
+ };
124
+
125
+ const riskLevel = impactReport.riskAssessment.overallRisk;
126
+ const thresholds = this.approvalThresholds[riskLevel];
127
+
128
+ // Basic approval requirements from risk level
129
+ if (thresholds) {
130
+ requirements.approval_needed = !thresholds.auto_approve;
131
+ requirements.requires_review = thresholds.requires_review;
132
+ requirements.requires_approval = thresholds.requires_approval;
133
+ requirements.requires_multiple_approvers = thresholds.requires_multiple_approvers;
134
+ }
135
+
136
+ // Component-specific rules
137
+ const componentRule = this.getComponentApprovalRule(impactReport.targetComponent);
138
+ if (componentRule) {
139
+ requirements.required_approvers = Math.max(requirements.required_approvers, componentRule.required_approvers);
140
+ requirements.timeout_hours = Math.max(requirements.timeout_hours, componentRule.timeout_hours);
141
+ }
142
+
143
+ // Modification-specific rules
144
+ const modificationRule = this.getModificationApprovalRule(impactReport.modificationType);
145
+ if (modificationRule) {
146
+ requirements.required_approvers = Math.max(requirements.required_approvers, modificationRule.required_approvers);
147
+ requirements.timeout_hours = Math.max(requirements.timeout_hours, modificationRule.timeout_hours);
148
+ }
149
+
150
+ // Critical issues that block auto-approval
151
+ if (impactReport.riskAssessment.criticalIssues.length > 0) {
152
+ requirements.approval_needed = true;
153
+ requirements.blocking_issues = impactReport.riskAssessment.criticalIssues.map(issue => issue.description);
154
+ }
155
+
156
+ // High-impact propagation
157
+ if (impactReport.propagationAnalysis.criticalPaths?.length > 2) {
158
+ requirements.approval_needed = true;
159
+ requirements.review_criteria.push('Multiple critical propagation paths require review');
160
+ }
161
+
162
+ // Many affected components
163
+ if (impactReport.summary.affectedComponents > 20) {
164
+ requirements.approval_needed = true;
165
+ requirements.review_criteria.push('Large number of affected components requires careful review');
166
+ }
167
+
168
+ // Breaking changes
169
+ const hasBreakingChanges = impactReport.propagationAnalysis.directEffects?.some(
170
+ effect => effect.changeType?.severity === 'breaking'
171
+ ) || false;
172
+
173
+ if (hasBreakingChanges) {
174
+ requirements.approval_needed = true;
175
+ requirements.required_approvers = Math.max(requirements.required_approvers, 2);
176
+ requirements.review_criteria.push('Breaking changes require multiple approvers');
177
+ }
178
+
179
+ // Security-sensitive modifications
180
+ if (impactReport.riskAssessment.riskDimensions?.security_risk?.score >= 6) {
181
+ requirements.approval_needed = true;
182
+ requirements.review_criteria.push('Security-sensitive modification requires approval');
183
+ }
184
+
185
+ return requirements;
186
+ }
187
+
188
+ /**
189
+ * Check if modification can be auto-approved
190
+ */
191
+ async checkAutoApproval(impactReport, requirements) {
192
+ const result = {
193
+ can_auto_approve: false,
194
+ reasons: [],
195
+ conditions_met: [],
196
+ conditions_failed: []
197
+ };
198
+
199
+ // Never auto-approve if manual approval is explicitly needed
200
+ if (requirements.approval_needed) {
201
+ result.reasons.push('Manual approval explicitly required due to risk level or critical issues');
202
+ return result;
203
+ }
204
+
205
+ // Never auto-approve critical risk modifications
206
+ if (impactReport.riskAssessment.overallRisk === 'critical') {
207
+ result.reasons.push('Critical risk level requires manual approval');
208
+ return result;
209
+ }
210
+
211
+ // Never auto-approve component removals
212
+ if (impactReport.modificationType === 'remove') {
213
+ result.reasons.push('Component removal always requires manual approval');
214
+ return result;
215
+ }
216
+
217
+ // Check auto-approval conditions
218
+ const autoApprovalConditions = await this.evaluateAutoApprovalConditions(impactReport);
219
+
220
+ if (autoApprovalConditions.all_conditions_met) {
221
+ result.can_auto_approve = true;
222
+ result.conditions_met = autoApprovalConditions.met_conditions;
223
+ result.reasons.push('All auto-approval conditions satisfied');
224
+ } else {
225
+ result.conditions_failed = autoApprovalConditions.failed_conditions;
226
+ result.reasons.push('Auto-approval conditions not met');
227
+ }
228
+
229
+ return result;
230
+ }
231
+
232
+ /**
233
+ * Evaluate auto-approval conditions
234
+ */
235
+ async evaluateAutoApprovalConditions(impactReport) {
236
+ const conditions = {
237
+ low_risk: impactReport.riskAssessment.overallRisk === 'low',
238
+ minimal_impact: impactReport.summary.affectedComponents <= 5,
239
+ no_critical_issues: impactReport.riskAssessment.criticalIssues.length === 0,
240
+ no_breaking_changes: !this.hasBreakingChanges(impactReport),
241
+ has_tests: await this.componentHasTests(impactReport.targetComponent),
242
+ small_change: this.isSmallChange(impactReport),
243
+ no_security_risk: impactReport.riskAssessment.riskDimensions?.security_risk?.score < 5
244
+ };
245
+
246
+ const metConditions = Object.entries(conditions)
247
+ .filter(([condition, met]) => met)
248
+ .map(([condition]) => condition);
249
+
250
+ const failedConditions = Object.entries(conditions)
251
+ .filter(([condition, met]) => !met)
252
+ .map(([condition]) => condition);
253
+
254
+ // Require at least 5 out of 7 conditions for auto-approval
255
+ const requiredConditions = 5;
256
+ const allConditionsMet = metConditions.length >= requiredConditions;
257
+
258
+ return {
259
+ all_conditions_met: allConditionsMet,
260
+ met_conditions: metConditions,
261
+ failed_conditions: failedConditions,
262
+ condition_score: `${metConditions.length}/${Object.keys(conditions).length}`
263
+ };
264
+ }
265
+
266
+ /**
267
+ * Execute auto-approval
268
+ */
269
+ async executeAutoApproval(impactReport, autoApprovalResult, requestId) {
270
+ const approval = {
271
+ request_id: requestId,
272
+ target_component: impactReport.targetComponent.path,
273
+ modification_type: impactReport.modificationType,
274
+ approval_status: 'auto_approved',
275
+ approval_type: 'automatic',
276
+ risk_level: impactReport.riskAssessment.overallRisk,
277
+ auto_approval_reasons: autoApprovalResult.reasons,
278
+ conditions_met: autoApprovalResult.conditions_met,
279
+ approved_by: 'system_auto_approval',
280
+ approved_at: new Date().toISOString(),
281
+ valid_until: this.calculateExpirationTime(24), // Auto-approvals valid for 24 hours
282
+ metadata: {
283
+ impact_summary: impactReport.summary,
284
+ approval_confidence: this.calculateApprovalConfidence(autoApprovalResult)
285
+ }
286
+ };
287
+
288
+ // Log approval
289
+ await this.logApproval(approval);
290
+
291
+ console.log(chalk.green(`✅ Auto-approved: ${impactReport.targetComponent.path}`));
292
+ console.log(chalk.gray(` Risk level: ${impactReport.riskAssessment.overallRisk}`));
293
+ console.log(chalk.gray(` Conditions met: ${autoApprovalResult.conditions_met.length}`));
294
+
295
+ return approval;
296
+ }
297
+
298
+ /**
299
+ * Execute manual approval process
300
+ */
301
+ async executeManualApproval(impactReport, requirements, config, requestId) {
302
+ console.log(chalk.yellow(`\n⚠️ MANUAL APPROVAL REQUIRED`));
303
+ console.log(chalk.gray(`Component: ${impactReport.targetComponent.path}`));
304
+ console.log(chalk.gray(`Risk Level: ${impactReport.riskAssessment.overallRisk.toUpperCase()}`));
305
+ console.log(chalk.gray(`Affected Components: ${impactReport.summary.affectedComponents}`));
306
+
307
+ if (requirements.blocking_issues.length > 0) {
308
+ console.log(chalk.red(`\nBlocking Issues:`));
309
+ requirements.blocking_issues.forEach((issue, index) => {
310
+ console.log(chalk.red(` ${index + 1}. ${issue}`));
311
+ });
312
+ }
313
+
314
+ if (requirements.review_criteria.length > 0) {
315
+ console.log(chalk.yellow(`\nReview Criteria:`));
316
+ requirements.review_criteria.forEach((criteria, index) => {
317
+ console.log(chalk.yellow(` ${index + 1}. ${criteria}`));
318
+ });
319
+ }
320
+
321
+ // Display key recommendations
322
+ if (impactReport.riskAssessment.recommendations.length > 0) {
323
+ console.log(chalk.blue(`\nKey Recommendations:`));
324
+ impactReport.riskAssessment.recommendations.slice(0, 3).forEach((rec, index) => {
325
+ console.log(chalk.blue(` ${index + 1}. ${rec.title}`));
326
+ console.log(chalk.gray(` ${rec.description}`));
327
+ });
328
+ }
329
+
330
+ // Approval prompt
331
+ const approvalQuestions = await this.buildApprovalQuestions(impactReport, requirements);
332
+ const approvalAnswers = await inquirer.prompt(approvalQuestions);
333
+
334
+ const approval = {
335
+ request_id: requestId,
336
+ target_component: impactReport.targetComponent.path,
337
+ modification_type: impactReport.modificationType,
338
+ approval_status: approvalAnswers.approved ? 'approved' : 'rejected',
339
+ approval_type: 'manual',
340
+ risk_level: impactReport.riskAssessment.overallRisk,
341
+ approved_by: approvalAnswers.approver_name || 'user',
342
+ approved_at: new Date().toISOString(),
343
+ approval_reason: approvalAnswers.approval_reason,
344
+ conditions_acknowledged: approvalAnswers.conditions_acknowledged || false,
345
+ valid_until: this.calculateExpirationTime(requirements.timeout_hours),
346
+ requirements_met: requirements,
347
+ metadata: {
348
+ impact_summary: impactReport.summary,
349
+ approval_answers: approvalAnswers
350
+ }
351
+ };
352
+
353
+ // Add approval conditions if approved
354
+ if (approvalAnswers.approved) {
355
+ approval.approval_conditions = approvalAnswers.approval_conditions;
356
+ approval.monitoring_required = approvalAnswers.monitoring_required || false;
357
+ approval.rollback_plan_required = approvalAnswers.rollback_plan || false;
358
+ } else {
359
+ approval.rejection_reason = approvalAnswers.rejection_reason;
360
+ approval.recommended_actions = approvalAnswers.recommended_actions;
361
+ }
362
+
363
+ // Log approval decision
364
+ await this.logApproval(approval);
365
+
366
+ if (approvalAnswers.approved) {
367
+ console.log(chalk.green(`\n✅ Manual approval granted`));
368
+ console.log(chalk.gray(` Approved by: ${approval.approved_by}`));
369
+ console.log(chalk.gray(` Valid until: ${new Date(approval.valid_until).toLocaleString()}`));
370
+
371
+ if (approval.approval_conditions) {
372
+ console.log(chalk.blue(` Conditions: ${approval.approval_conditions}`));
373
+ }
374
+ } else {
375
+ console.log(chalk.red(`\n❌ Approval rejected`));
376
+ console.log(chalk.gray(` Reason: ${approval.rejection_reason}`));
377
+ }
378
+
379
+ return approval;
380
+ }
381
+
382
+ /**
383
+ * Build approval question flow
384
+ */
385
+ async buildApprovalQuestions(impactReport, requirements) {
386
+ const questions = [];
387
+
388
+ // Main approval question
389
+ questions.push({
390
+ type: 'confirm',
391
+ name: 'approved',
392
+ message: `Approve ${impactReport.modificationType} of ${impactReport.targetComponent.path}?`,
393
+ default: false
394
+ });
395
+
396
+ // Conditional questions based on approval
397
+ questions.push({
398
+ type: 'input',
399
+ name: 'approver_name',
400
+ message: 'Enter your name/identifier:',
401
+ when: (answers) => answers.approved,
402
+ validate: (input) => input.length > 0 || 'Name is required'
403
+ });
404
+
405
+ questions.push({
406
+ type: 'input',
407
+ name: 'approval_reason',
408
+ message: 'Reason for approval:',
409
+ when: (answers) => answers.approved,
410
+ default: 'Impact analysis reviewed and acceptable'
411
+ });
412
+
413
+ // High-risk additional questions
414
+ if (requirements.risk_level === 'high' || requirements.risk_level === 'critical') {
415
+ questions.push({
416
+ type: 'confirm',
417
+ name: 'conditions_acknowledged',
418
+ message: 'Do you acknowledge all risk factors and recommendations?',
419
+ when: (answers) => answers.approved,
420
+ default: false
421
+ });
422
+
423
+ questions.push({
424
+ type: 'input',
425
+ name: 'approval_conditions',
426
+ message: 'Enter any approval conditions or requirements:',
427
+ when: (answers) => answers.approved && answers.conditions_acknowledged,
428
+ default: 'Standard monitoring and rollback procedures apply'
429
+ });
430
+
431
+ questions.push({
432
+ type: 'confirm',
433
+ name: 'monitoring_required',
434
+ message: 'Require enhanced monitoring after deployment?',
435
+ when: (answers) => answers.approved,
436
+ default: true
437
+ });
438
+
439
+ questions.push({
440
+ type: 'confirm',
441
+ name: 'rollback_plan',
442
+ message: 'Require documented rollback plan?',
443
+ when: (answers) => answers.approved,
444
+ default: true
445
+ });
446
+ }
447
+
448
+ // Rejection questions
449
+ questions.push({
450
+ type: 'input',
451
+ name: 'rejection_reason',
452
+ message: 'Reason for rejection:',
453
+ when: (answers) => !answers.approved,
454
+ validate: (input) => input.length > 0 || 'Rejection reason is required'
455
+ });
456
+
457
+ questions.push({
458
+ type: 'input',
459
+ name: 'recommended_actions',
460
+ message: 'Recommended actions before resubmission:',
461
+ when: (answers) => !answers.approved,
462
+ default: 'Address critical issues and reduce risk factors'
463
+ });
464
+
465
+ return questions;
466
+ }
467
+
468
+ /**
469
+ * Log approval decision for audit trail
470
+ */
471
+ async logApproval(approval) {
472
+ // Add to approval history
473
+ this.approvalHistory.push({
474
+ request_id: approval.request_id,
475
+ component: approval.target_component,
476
+ status: approval.approval_status,
477
+ risk_level: approval.risk_level,
478
+ approved_by: approval.approved_by,
479
+ timestamp: approval.approved_at
480
+ });
481
+
482
+ // Write to audit log file
483
+ try {
484
+ const logDir = path.join(this.rootPath, '.aios', 'audit');
485
+ await fs.mkdir(logDir, { recursive: true });
486
+
487
+ const logFile = path.join(logDir, 'approval_log.jsonl');
488
+ const logEntry = JSON.stringify(approval) + '\n';
489
+
490
+ await fs.appendFile(logFile, logEntry);
491
+
492
+ console.log(chalk.gray(` Approval logged to audit trail`));
493
+
494
+ } catch (_error) {
495
+ console.warn(chalk.yellow(`Failed to write approval log: ${error.message}`));
496
+ }
497
+ }
498
+
499
+ // Helper methods
500
+
501
+ getComponentApprovalRule(component) {
502
+ if (component.type === 'agent') {
503
+ return this.approvalRules.get('agent_modification');
504
+ } else if (component.type === 'workflow') {
505
+ return this.approvalRules.get('workflow_modification');
506
+ } else if (component.type === 'util' && component.path.includes('core')) {
507
+ return this.approvalRules.get('core_util_modification');
508
+ }
509
+ return null;
510
+ }
511
+
512
+ getModificationApprovalRule(modificationType) {
513
+ if (modificationType === 'remove') {
514
+ return this.approvalRules.get('component_removal');
515
+ }
516
+ return null;
517
+ }
518
+
519
+ hasBreakingChanges(impactReport) {
520
+ return impactReport.propagationAnalysis.directEffects?.some(
521
+ effect => effect.changeType?.severity === 'breaking'
522
+ ) || false;
523
+ }
524
+
525
+ async componentHasTests(component) {
526
+ const testPaths = [
527
+ path.join(this.rootPath, 'tests', 'unit', component.type, `${component.name}.test.js`),
528
+ path.join(this.rootPath, 'tests', 'integration', component.type, `${component.name}.integration.test.js`),
529
+ path.join(this.rootPath, 'test', `${component.name}.test.js`)
530
+ ];
531
+
532
+ for (const testPath of testPaths) {
533
+ try {
534
+ await fs.access(testPath);
535
+ return true;
536
+ } catch (_error) {
537
+ // File doesn't exist, continue
538
+ }
539
+ }
540
+
541
+ return false;
542
+ }
543
+
544
+ isSmallChange(impactReport) {
545
+ return impactReport.summary.affectedComponents <= 3 &&
546
+ impactReport.summary.propagationDepth <= 2;
547
+ }
548
+
549
+ calculateExpirationTime(hours) {
550
+ const now = new Date();
551
+ now.setHours(now.getHours() + hours);
552
+ return now.toISOString();
553
+ }
554
+
555
+ calculateApprovalConfidence(autoApprovalResult) {
556
+ const conditionsMet = autoApprovalResult.conditions_met.length;
557
+ const totalConditions = 7; // Based on evaluateAutoApprovalConditions
558
+ return Math.round((conditionsMet / totalConditions) * 100);
559
+ }
560
+
561
+ /**
562
+ * Check if approval is still valid
563
+ */
564
+ isApprovalValid(approval) {
565
+ const now = new Date();
566
+ const validUntil = new Date(approval.valid_until);
567
+ return now < validUntil;
568
+ }
569
+
570
+ /**
571
+ * Get approval history
572
+ */
573
+ getApprovalHistory(options = {}) {
574
+ const history = {
575
+ total_approvals: this.approvalHistory.length,
576
+ approval_stats: this.calculateApprovalStats(),
577
+ recent_approvals: this.approvalHistory.slice(-10)
578
+ };
579
+
580
+ if (options.component) {
581
+ history.component_approvals = this.approvalHistory.filter(
582
+ approval => approval.component === options.component
583
+ );
584
+ }
585
+
586
+ if (options.risk_level) {
587
+ history.risk_level_approvals = this.approvalHistory.filter(
588
+ approval => approval.risk_level === options.risk_level
589
+ );
590
+ }
591
+
592
+ return history;
593
+ }
594
+
595
+ calculateApprovalStats() {
596
+ const stats = {
597
+ approved: 0,
598
+ rejected: 0,
599
+ auto_approved: 0,
600
+ by_risk_level: { low: 0, medium: 0, high: 0, critical: 0 }
601
+ };
602
+
603
+ this.approvalHistory.forEach(approval => {
604
+ if (approval.status === 'approved') stats.approved++;
605
+ else if (approval.status === 'rejected') stats.rejected++;
606
+ else if (approval.status === 'auto_approved') stats.auto_approved++;
607
+
608
+ stats.by_risk_level[approval.risk_level]++;
609
+ });
610
+
611
+ return stats;
612
+ }
613
+
614
+ /**
615
+ * Get pending approvals
616
+ */
617
+ getPendingApprovals() {
618
+ return Array.from(this.pendingApprovals.values());
619
+ }
620
+
621
+ /**
622
+ * Clear expired approvals
623
+ */
624
+ clearExpiredApprovals() {
625
+ const now = new Date();
626
+ let clearedCount = 0;
627
+
628
+ for (const [requestId, approval] of this.pendingApprovals) {
629
+ if (new Date(approval.valid_until) < now) {
630
+ this.pendingApprovals.delete(requestId);
631
+ clearedCount++;
632
+ }
633
+ }
634
+
635
+ if (clearedCount > 0) {
636
+ console.log(chalk.gray(`Cleared ${clearedCount} expired approvals`));
637
+ }
638
+
639
+ return clearedCount;
640
+ }
641
+ }
642
+
643
643
  module.exports = ApprovalWorkflow;