aios-core 4.2.6 → 4.2.8
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/.aios-core/core/orchestration/context-manager.js +333 -5
- package/.aios-core/core/orchestration/dashboard-integration.js +17 -1
- package/.aios-core/core/orchestration/execution-profile-resolver.js +107 -0
- package/.aios-core/core/orchestration/index.js +3 -0
- package/.aios-core/core/orchestration/skill-dispatcher.js +2 -0
- package/.aios-core/core/orchestration/subagent-prompt-builder.js +2 -0
- package/.aios-core/core/orchestration/workflow-orchestrator.js +113 -5
- package/.aios-core/data/entity-registry.yaml +1114 -1336
- package/.aios-core/development/agents/ux-design-expert.md +1 -1
- package/.aios-core/development/checklists/brownfield-compatibility-checklist.md +114 -0
- package/.aios-core/development/scripts/workflow-state-manager.js +128 -1
- package/.aios-core/development/tasks/next.md +36 -5
- package/.aios-core/hooks/ids-post-commit.js +29 -1
- package/.aios-core/hooks/ids-pre-push.js +29 -1
- package/.aios-core/infrastructure/contracts/compatibility/aios-4.0.4.yaml +44 -0
- package/.aios-core/infrastructure/scripts/validate-parity.js +238 -2
- package/.aios-core/install-manifest.yaml +43 -27
- package/.aios-core/product/templates/brownfield-risk-report-tmpl.yaml +277 -0
- package/.aios-core/workflow-intelligence/engine/suggestion-engine.js +114 -5
- package/LICENSE +13 -1
- package/README.md +39 -9
- package/package.json +9 -7
- package/packages/installer/src/pro/pro-scaffolder.js +50 -0
- package/packages/installer/src/wizard/ide-config-generator.js +0 -117
- package/packages/installer/src/wizard/index.js +2 -118
- package/packages/installer/src/wizard/pro-setup.js +50 -5
- package/scripts/semantic-lint.js +190 -0
- package/.claude/hooks/pre-commit-mmos-guard.sh +0 -99
|
@@ -32,6 +32,8 @@ class ContextManager {
|
|
|
32
32
|
// State file path
|
|
33
33
|
this.stateDir = path.join(projectRoot, '.aios', 'workflow-state');
|
|
34
34
|
this.statePath = path.join(this.stateDir, `${workflowId}.json`);
|
|
35
|
+
this.handoffDir = path.join(this.stateDir, 'handoffs');
|
|
36
|
+
this.confidenceDir = path.join(this.stateDir, 'confidence');
|
|
35
37
|
|
|
36
38
|
// In-memory cache
|
|
37
39
|
this._stateCache = null;
|
|
@@ -43,6 +45,8 @@ class ContextManager {
|
|
|
43
45
|
*/
|
|
44
46
|
async ensureStateDir() {
|
|
45
47
|
await fs.ensureDir(this.stateDir);
|
|
48
|
+
await fs.ensureDir(this.handoffDir);
|
|
49
|
+
await fs.ensureDir(this.confidenceDir);
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
/**
|
|
@@ -76,6 +80,7 @@ class ContextManager {
|
|
|
76
80
|
phases: {},
|
|
77
81
|
metadata: {
|
|
78
82
|
projectRoot: this.projectRoot,
|
|
83
|
+
delivery_confidence: null,
|
|
79
84
|
},
|
|
80
85
|
};
|
|
81
86
|
}
|
|
@@ -113,19 +118,25 @@ class ContextManager {
|
|
|
113
118
|
* @param {number} phaseNum - Phase number
|
|
114
119
|
* @param {Object} output - Phase output data
|
|
115
120
|
*/
|
|
116
|
-
async savePhaseOutput(phaseNum, output) {
|
|
121
|
+
async savePhaseOutput(phaseNum, output, options = {}) {
|
|
117
122
|
const state = await this.loadState();
|
|
123
|
+
const completedAt = new Date().toISOString();
|
|
124
|
+
state.currentPhase = phaseNum;
|
|
125
|
+
state.status = 'in_progress';
|
|
126
|
+
const handoff = this._buildHandoffPackage(phaseNum, output, state, options, completedAt);
|
|
118
127
|
|
|
119
128
|
state.phases[phaseNum] = {
|
|
120
129
|
...output,
|
|
121
|
-
completedAt
|
|
130
|
+
completedAt,
|
|
131
|
+
handoff,
|
|
122
132
|
};
|
|
123
|
-
|
|
124
|
-
state.
|
|
125
|
-
state.status = 'in_progress';
|
|
133
|
+
state.metadata = state.metadata || {};
|
|
134
|
+
state.metadata.delivery_confidence = this._calculateDeliveryConfidence(state);
|
|
126
135
|
|
|
127
136
|
this._stateCache = state;
|
|
128
137
|
await this._saveState();
|
|
138
|
+
await this._saveHandoffFile(handoff);
|
|
139
|
+
await this._saveConfidenceFile(state.metadata.delivery_confidence);
|
|
129
140
|
}
|
|
130
141
|
|
|
131
142
|
/**
|
|
@@ -145,10 +156,18 @@ class ContextManager {
|
|
|
145
156
|
}
|
|
146
157
|
}
|
|
147
158
|
|
|
159
|
+
const previousHandoffs = {};
|
|
160
|
+
for (const [phaseId, phaseData] of Object.entries(previousPhases)) {
|
|
161
|
+
if (phaseData && phaseData.handoff) {
|
|
162
|
+
previousHandoffs[phaseId] = phaseData.handoff;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
148
166
|
return {
|
|
149
167
|
workflowId: this.workflowId,
|
|
150
168
|
currentPhase: phaseNum,
|
|
151
169
|
previousPhases,
|
|
170
|
+
previousHandoffs,
|
|
152
171
|
metadata: state.metadata,
|
|
153
172
|
};
|
|
154
173
|
}
|
|
@@ -247,9 +266,318 @@ class ContextManager {
|
|
|
247
266
|
currentPhase: state.currentPhase,
|
|
248
267
|
completedPhases: phases,
|
|
249
268
|
totalPhases: phases.length,
|
|
269
|
+
deliveryConfidence: state.metadata?.delivery_confidence || null,
|
|
250
270
|
};
|
|
251
271
|
}
|
|
252
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Get latest delivery confidence score metadata.
|
|
275
|
+
* @returns {Object|null} Confidence payload
|
|
276
|
+
*/
|
|
277
|
+
getDeliveryConfidence() {
|
|
278
|
+
return this._stateCache?.metadata?.delivery_confidence || null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Build standardized handoff package for inter-agent transfer.
|
|
283
|
+
* @private
|
|
284
|
+
*/
|
|
285
|
+
_buildHandoffPackage(phaseNum, output, state, options, completedAt) {
|
|
286
|
+
const handoffTarget = options.handoffTarget || {};
|
|
287
|
+
const decision = this._extractDecisionLog(output);
|
|
288
|
+
const evidence = this._extractEvidenceLinks(output);
|
|
289
|
+
const risks = this._extractOpenRisks(output);
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
version: '1.0.0',
|
|
293
|
+
workflow_id: this.workflowId,
|
|
294
|
+
generated_at: completedAt,
|
|
295
|
+
from: {
|
|
296
|
+
phase: phaseNum,
|
|
297
|
+
agent: output.agent || null,
|
|
298
|
+
action: output.action || null,
|
|
299
|
+
task: output.task || null,
|
|
300
|
+
},
|
|
301
|
+
to: {
|
|
302
|
+
phase: handoffTarget.phase || null,
|
|
303
|
+
agent: handoffTarget.agent || null,
|
|
304
|
+
},
|
|
305
|
+
context_snapshot: {
|
|
306
|
+
workflow_status: state.status,
|
|
307
|
+
current_phase: state.currentPhase,
|
|
308
|
+
metadata: state.metadata || {},
|
|
309
|
+
},
|
|
310
|
+
decision_log: decision,
|
|
311
|
+
evidence_links: evidence,
|
|
312
|
+
open_risks: risks,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Persist handoff package as a dedicated artifact.
|
|
318
|
+
* @private
|
|
319
|
+
*/
|
|
320
|
+
async _saveHandoffFile(handoff) {
|
|
321
|
+
const phase = handoff?.from?.phase || 'unknown';
|
|
322
|
+
const filePath = path.join(this.handoffDir, `${this.workflowId}-phase-${phase}.handoff.json`);
|
|
323
|
+
await fs.ensureDir(this.handoffDir);
|
|
324
|
+
await fs.writeJson(filePath, handoff, { spaces: 2 });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Persist confidence score as a dedicated artifact.
|
|
329
|
+
* @private
|
|
330
|
+
*/
|
|
331
|
+
async _saveConfidenceFile(confidence) {
|
|
332
|
+
if (!confidence) return;
|
|
333
|
+
const filePath = path.join(this.confidenceDir, `${this.workflowId}.delivery-confidence.json`);
|
|
334
|
+
await fs.ensureDir(this.confidenceDir);
|
|
335
|
+
await fs.writeJson(filePath, confidence, { spaces: 2 });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @private
|
|
340
|
+
*/
|
|
341
|
+
_extractDecisionLog(output = {}) {
|
|
342
|
+
const result = output.result || {};
|
|
343
|
+
const entries = Array.isArray(result.decisions)
|
|
344
|
+
? result.decisions
|
|
345
|
+
: Array.isArray(result.decision_log)
|
|
346
|
+
? result.decision_log
|
|
347
|
+
: [];
|
|
348
|
+
const sourcePaths = [];
|
|
349
|
+
|
|
350
|
+
if (result.decisionLogPath) sourcePaths.push(result.decisionLogPath);
|
|
351
|
+
if (result.decision_log_path) sourcePaths.push(result.decision_log_path);
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
entries,
|
|
355
|
+
source_paths: sourcePaths,
|
|
356
|
+
count: entries.length,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @private
|
|
362
|
+
*/
|
|
363
|
+
_extractEvidenceLinks(output = {}) {
|
|
364
|
+
const evidence = [];
|
|
365
|
+
const result = output.result || {};
|
|
366
|
+
const validation = output.validation || {};
|
|
367
|
+
|
|
368
|
+
if (Array.isArray(result.evidence_links)) {
|
|
369
|
+
evidence.push(...result.evidence_links);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (Array.isArray(validation.checks)) {
|
|
373
|
+
for (const check of validation.checks) {
|
|
374
|
+
if (check.path) evidence.push(check.path);
|
|
375
|
+
if (check.checklist) evidence.push(check.checklist);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return [...new Set(evidence)];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* @private
|
|
384
|
+
*/
|
|
385
|
+
_extractOpenRisks(output = {}) {
|
|
386
|
+
const result = output.result || {};
|
|
387
|
+
const risks = [];
|
|
388
|
+
|
|
389
|
+
if (Array.isArray(result.open_risks)) {
|
|
390
|
+
risks.push(...result.open_risks);
|
|
391
|
+
}
|
|
392
|
+
if (Array.isArray(result.risks)) {
|
|
393
|
+
risks.push(...result.risks);
|
|
394
|
+
}
|
|
395
|
+
if (Array.isArray(result.risk_register)) {
|
|
396
|
+
risks.push(...result.risk_register);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return risks;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Compute delivery confidence score from workflow state.
|
|
404
|
+
* @private
|
|
405
|
+
*/
|
|
406
|
+
_calculateDeliveryConfidence(state) {
|
|
407
|
+
const phases = Object.values(state.phases || {});
|
|
408
|
+
const weights = {
|
|
409
|
+
test_coverage: 0.25,
|
|
410
|
+
ac_completion: 0.30,
|
|
411
|
+
risk_score_inv: 0.20,
|
|
412
|
+
debt_score_inv: 0.15,
|
|
413
|
+
regression_clear: 0.10,
|
|
414
|
+
};
|
|
415
|
+
const components = {
|
|
416
|
+
test_coverage: this._calculateTestCoverage(phases),
|
|
417
|
+
ac_completion: this._calculateAcCompletion(phases),
|
|
418
|
+
risk_score_inv: this._calculateRiskInverseScore(phases),
|
|
419
|
+
debt_score_inv: this._calculateDebtInverseScore(phases),
|
|
420
|
+
regression_clear: this._calculateRegressionClear(phases),
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const scoreBase = Object.keys(weights).reduce(
|
|
424
|
+
(acc, key) => acc + (components[key] || 0) * weights[key],
|
|
425
|
+
0,
|
|
426
|
+
);
|
|
427
|
+
const score = Number((scoreBase * 100).toFixed(2));
|
|
428
|
+
const threshold = this._resolveConfidenceThreshold();
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
version: '1.0.0',
|
|
432
|
+
calculated_at: new Date().toISOString(),
|
|
433
|
+
score,
|
|
434
|
+
threshold,
|
|
435
|
+
gate_passed: score >= threshold,
|
|
436
|
+
formula: {
|
|
437
|
+
expression:
|
|
438
|
+
'confidence = (test_coverage*0.25 + ac_completion*0.30 + risk_score_inv*0.20 + debt_score_inv*0.15 + regression_clear*0.10) * 100',
|
|
439
|
+
weights,
|
|
440
|
+
},
|
|
441
|
+
components,
|
|
442
|
+
phase_count: phases.length,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* @private
|
|
448
|
+
*/
|
|
449
|
+
_resolveConfidenceThreshold() {
|
|
450
|
+
const raw = process.env.AIOS_DELIVERY_CONFIDENCE_THRESHOLD;
|
|
451
|
+
const parsed = Number(raw);
|
|
452
|
+
return Number.isFinite(parsed) ? parsed : 70;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* @private
|
|
457
|
+
*/
|
|
458
|
+
_calculateTestCoverage(phases) {
|
|
459
|
+
let totalChecks = 0;
|
|
460
|
+
let passedChecks = 0;
|
|
461
|
+
|
|
462
|
+
for (const phase of phases) {
|
|
463
|
+
const checks = phase?.validation?.checks;
|
|
464
|
+
if (!Array.isArray(checks)) continue;
|
|
465
|
+
for (const check of checks) {
|
|
466
|
+
totalChecks += 1;
|
|
467
|
+
if (check?.passed === true) passedChecks += 1;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (totalChecks === 0) {
|
|
472
|
+
return phases.length > 0 ? 1 : 0;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return passedChecks / totalChecks;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* @private
|
|
480
|
+
*/
|
|
481
|
+
_calculateAcCompletion(phases) {
|
|
482
|
+
let total = 0;
|
|
483
|
+
let done = 0;
|
|
484
|
+
let hasExplicitData = false;
|
|
485
|
+
|
|
486
|
+
for (const phase of phases) {
|
|
487
|
+
const result = phase?.result || {};
|
|
488
|
+
if (Number.isFinite(result.ac_total) && Number.isFinite(result.ac_completed)) {
|
|
489
|
+
hasExplicitData = true;
|
|
490
|
+
total += Math.max(0, result.ac_total);
|
|
491
|
+
done += Math.min(Math.max(0, result.ac_completed), Math.max(0, result.ac_total));
|
|
492
|
+
} else if (Array.isArray(result.acceptance_criteria)) {
|
|
493
|
+
hasExplicitData = true;
|
|
494
|
+
total += result.acceptance_criteria.length;
|
|
495
|
+
done += result.acceptance_criteria.filter((item) => item?.done || item?.status === 'done').length;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (hasExplicitData && total > 0) {
|
|
500
|
+
return done / total;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (phases.length === 0) {
|
|
504
|
+
return 0;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const successful = phases.filter((phase) => phase?.result?.status !== 'failed').length;
|
|
508
|
+
return successful / phases.length;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* @private
|
|
513
|
+
*/
|
|
514
|
+
_calculateRiskInverseScore(phases) {
|
|
515
|
+
const totalRisks = phases.reduce((sum, phase) => {
|
|
516
|
+
const handoffRisks = Array.isArray(phase?.handoff?.open_risks) ? phase.handoff.open_risks.length : 0;
|
|
517
|
+
const resultRisks = this._extractOpenRisks(phase).length;
|
|
518
|
+
return sum + Math.max(handoffRisks, resultRisks);
|
|
519
|
+
}, 0);
|
|
520
|
+
|
|
521
|
+
return Math.max(0, 1 - totalRisks / 10);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* @private
|
|
526
|
+
*/
|
|
527
|
+
_calculateDebtInverseScore(phases) {
|
|
528
|
+
const totalDebt = phases.reduce((sum, phase) => {
|
|
529
|
+
const result = phase?.result || {};
|
|
530
|
+
const explicitCount = Number.isFinite(result.technical_debt_count)
|
|
531
|
+
? result.technical_debt_count
|
|
532
|
+
: Number.isFinite(result.debt_count)
|
|
533
|
+
? result.debt_count
|
|
534
|
+
: 0;
|
|
535
|
+
const listCount = [
|
|
536
|
+
result.technical_debt,
|
|
537
|
+
result.debt_items,
|
|
538
|
+
result.todos,
|
|
539
|
+
result.hacks,
|
|
540
|
+
].reduce((listSum, list) => listSum + (Array.isArray(list) ? list.length : 0), 0);
|
|
541
|
+
return sum + explicitCount + listCount;
|
|
542
|
+
}, 0);
|
|
543
|
+
|
|
544
|
+
return Math.max(0, 1 - totalDebt / 10);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* @private
|
|
549
|
+
*/
|
|
550
|
+
_calculateRegressionClear(phases) {
|
|
551
|
+
let totalRegressionChecks = 0;
|
|
552
|
+
let passedRegressionChecks = 0;
|
|
553
|
+
|
|
554
|
+
for (const phase of phases) {
|
|
555
|
+
const checks = phase?.validation?.checks;
|
|
556
|
+
if (!Array.isArray(checks)) continue;
|
|
557
|
+
|
|
558
|
+
for (const check of checks) {
|
|
559
|
+
const type = String(check?.type || '').toLowerCase();
|
|
560
|
+
const pathValue = String(check?.path || '').toLowerCase();
|
|
561
|
+
const checklist = String(check?.checklist || '').toLowerCase();
|
|
562
|
+
const isRegression = type.includes('regression')
|
|
563
|
+
|| pathValue.includes('regression')
|
|
564
|
+
|| checklist.includes('regression');
|
|
565
|
+
|
|
566
|
+
if (!isRegression) continue;
|
|
567
|
+
totalRegressionChecks += 1;
|
|
568
|
+
if (check?.passed === true) {
|
|
569
|
+
passedRegressionChecks += 1;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (totalRegressionChecks === 0) {
|
|
575
|
+
return this._calculateTestCoverage(phases);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return passedRegressionChecks / totalRegressionChecks;
|
|
579
|
+
}
|
|
580
|
+
|
|
253
581
|
/**
|
|
254
582
|
* Reset workflow state (for re-execution)
|
|
255
583
|
* @param {boolean} keepMetadata - Whether to preserve metadata
|
|
@@ -252,12 +252,28 @@ class DashboardIntegration extends EventEmitter {
|
|
|
252
252
|
await fs.writeJson(this.statusPath, status, { spaces: 2 });
|
|
253
253
|
this.emit('statusUpdated', status);
|
|
254
254
|
} catch (error) {
|
|
255
|
-
this.
|
|
255
|
+
this._emitSafeError({ type: 'statusUpdate', error });
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
return status;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
/**
|
|
262
|
+
* Emit error event only when listeners are present, otherwise degrade to warning.
|
|
263
|
+
* Avoids unhandled EventEmitter 'error' exceptions in background update flows.
|
|
264
|
+
* @private
|
|
265
|
+
* @param {Object} payload
|
|
266
|
+
*/
|
|
267
|
+
_emitSafeError(payload) {
|
|
268
|
+
if (this.listenerCount('error') > 0) {
|
|
269
|
+
this.emit('error', payload);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const message = payload?.error?.message || 'unknown dashboard error';
|
|
274
|
+
console.warn(`[DashboardIntegration] ${payload?.type || 'error'}: ${message}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
261
277
|
/**
|
|
262
278
|
* Build status object (AC2)
|
|
263
279
|
* @returns {Object} Status object
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const VALID_PROFILES = ['safe', 'balanced', 'aggressive'];
|
|
4
|
+
const VALID_CONTEXTS = ['production', 'migration', 'security-sensitive', 'development'];
|
|
5
|
+
|
|
6
|
+
const PROFILE_POLICIES = {
|
|
7
|
+
safe: {
|
|
8
|
+
require_confirmation: true,
|
|
9
|
+
require_tests_before_handoff: true,
|
|
10
|
+
max_parallel_changes: 1,
|
|
11
|
+
allow_destructive_operations: false,
|
|
12
|
+
allow_autonomous_refactors: false,
|
|
13
|
+
},
|
|
14
|
+
balanced: {
|
|
15
|
+
require_confirmation: 'high-risk-only',
|
|
16
|
+
require_tests_before_handoff: true,
|
|
17
|
+
max_parallel_changes: 3,
|
|
18
|
+
allow_destructive_operations: false,
|
|
19
|
+
allow_autonomous_refactors: true,
|
|
20
|
+
},
|
|
21
|
+
aggressive: {
|
|
22
|
+
require_confirmation: false,
|
|
23
|
+
require_tests_before_handoff: false,
|
|
24
|
+
max_parallel_changes: 8,
|
|
25
|
+
allow_destructive_operations: false,
|
|
26
|
+
allow_autonomous_refactors: true,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function normalizeProfile(profile) {
|
|
31
|
+
const value = String(profile || '').trim().toLowerCase();
|
|
32
|
+
return VALID_PROFILES.includes(value) ? value : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeContext(context) {
|
|
36
|
+
const value = String(context || '').trim().toLowerCase();
|
|
37
|
+
return VALID_CONTEXTS.includes(value) ? value : 'development';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveExecutionProfile(input = {}) {
|
|
41
|
+
const context = normalizeContext(input.context);
|
|
42
|
+
const explicitProfile = normalizeProfile(input.explicitProfile);
|
|
43
|
+
const yolo = Boolean(input.yolo);
|
|
44
|
+
const reasons = [];
|
|
45
|
+
|
|
46
|
+
if (explicitProfile) {
|
|
47
|
+
reasons.push(`explicit profile selected: ${explicitProfile}`);
|
|
48
|
+
return {
|
|
49
|
+
profile: explicitProfile,
|
|
50
|
+
context,
|
|
51
|
+
policy: PROFILE_POLICIES[explicitProfile],
|
|
52
|
+
reasons,
|
|
53
|
+
source: 'explicit',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (context === 'production' || context === 'security-sensitive') {
|
|
58
|
+
reasons.push(`context "${context}" enforces safe profile`);
|
|
59
|
+
return {
|
|
60
|
+
profile: 'safe',
|
|
61
|
+
context,
|
|
62
|
+
policy: PROFILE_POLICIES.safe,
|
|
63
|
+
reasons,
|
|
64
|
+
source: 'context',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (context === 'migration') {
|
|
69
|
+
reasons.push('migration context enforces balanced profile');
|
|
70
|
+
return {
|
|
71
|
+
profile: 'balanced',
|
|
72
|
+
context,
|
|
73
|
+
policy: PROFILE_POLICIES.balanced,
|
|
74
|
+
reasons,
|
|
75
|
+
source: 'context',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (yolo) {
|
|
80
|
+
reasons.push('yolo mode enabled for non-critical context');
|
|
81
|
+
return {
|
|
82
|
+
profile: 'aggressive',
|
|
83
|
+
context,
|
|
84
|
+
policy: PROFILE_POLICIES.aggressive,
|
|
85
|
+
reasons,
|
|
86
|
+
source: 'yolo',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
reasons.push('default profile for standard development context');
|
|
91
|
+
return {
|
|
92
|
+
profile: 'balanced',
|
|
93
|
+
context,
|
|
94
|
+
policy: PROFILE_POLICIES.balanced,
|
|
95
|
+
reasons,
|
|
96
|
+
source: 'default',
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
VALID_PROFILES,
|
|
102
|
+
VALID_CONTEXTS,
|
|
103
|
+
PROFILE_POLICIES,
|
|
104
|
+
normalizeProfile,
|
|
105
|
+
normalizeContext,
|
|
106
|
+
resolveExecutionProfile,
|
|
107
|
+
};
|
|
@@ -22,6 +22,7 @@ const ParallelExecutor = require('./parallel-executor');
|
|
|
22
22
|
const TechStackDetector = require('./tech-stack-detector');
|
|
23
23
|
const ConditionEvaluator = require('./condition-evaluator');
|
|
24
24
|
const SkillDispatcher = require('./skill-dispatcher');
|
|
25
|
+
const executionProfileResolver = require('./execution-profile-resolver');
|
|
25
26
|
|
|
26
27
|
// Epic 0: Master Orchestrator (ADE)
|
|
27
28
|
const MasterOrchestrator = require('./master-orchestrator');
|
|
@@ -159,6 +160,8 @@ module.exports = {
|
|
|
159
160
|
TechStackDetector,
|
|
160
161
|
ConditionEvaluator,
|
|
161
162
|
SkillDispatcher,
|
|
163
|
+
ExecutionProfileResolver: executionProfileResolver,
|
|
164
|
+
resolveExecutionProfile: executionProfileResolver.resolveExecutionProfile,
|
|
162
165
|
|
|
163
166
|
// Epic 0: Orchestrator constants
|
|
164
167
|
OrchestratorState: MasterOrchestrator.OrchestratorState,
|
|
@@ -170,6 +170,8 @@ class SkillDispatcher {
|
|
|
170
170
|
workflowId: context.workflowId,
|
|
171
171
|
yoloMode: context.yoloMode || false,
|
|
172
172
|
previousPhases: context.previousPhases || {},
|
|
173
|
+
executionProfile: context.executionProfile || null,
|
|
174
|
+
executionPolicy: context.executionPolicy || null,
|
|
173
175
|
},
|
|
174
176
|
};
|
|
175
177
|
}
|
|
@@ -299,7 +299,9 @@ ${agentDef}
|
|
|
299
299
|
**Task File:** ${taskFile}
|
|
300
300
|
**Expected Output:** ${context.creates || 'See task definition'}
|
|
301
301
|
**Execution Mode:** ${context.yoloMode ? 'YOLO (autonomous)' : 'Interactive'}
|
|
302
|
+
**Execution Profile:** ${context.executionProfile || 'balanced'}
|
|
302
303
|
**Elicitation Required:** ${context.elicit ? 'Yes' : 'No'}
|
|
304
|
+
**Risk Policy:** ${JSON.stringify(context.executionPolicy || {}, null, 0)}
|
|
303
305
|
|
|
304
306
|
### Complete Task Definition:
|
|
305
307
|
|