scene-capability-engine 3.3.4 → 3.3.10
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/CHANGELOG.md +151 -78
- package/README.md +6 -6
- package/README.zh.md +6 -6
- package/bin/scene-capability-engine.js +129 -7
- package/docs/331-poc-adaptation-roadmap.md +3 -3
- package/docs/331-poc-dual-track-integration-guide.md +8 -8
- package/docs/331-poc-weekly-delivery-checklist.md +6 -6
- package/docs/README.md +6 -0
- package/docs/adopt-migration-guide.md +13 -13
- package/docs/adoption-guide.md +28 -28
- package/docs/agent-hooks-analysis.md +10 -10
- package/docs/agent-runtime/agent-result-summary-contract.schema.json +50 -0
- package/docs/agent-runtime/agent-result-summary.sample.json +11 -0
- package/docs/agent-runtime/capability-mapping-report.sample.json +42 -0
- package/docs/agent-runtime/capability-mapping-report.schema.json +136 -0
- package/docs/agent-runtime/failure-taxonomy-baseline.json +99 -0
- package/docs/agent-runtime/multi-agent-coordination-policy-baseline.json +38 -0
- package/docs/agent-runtime/strategy-routing-policy-baseline.json +24 -0
- package/docs/agent-runtime/symbol-evidence.sample.json +34 -0
- package/docs/agent-runtime/symbol-evidence.schema.json +128 -0
- package/docs/architecture.md +13 -13
- package/docs/articles/ai-driven-development-philosophy-and-practice.en.md +3 -3
- package/docs/articles/ai-driven-development-philosophy-and-practice.md +3 -3
- package/docs/autonomous-control-guide.md +35 -35
- package/docs/command-reference.md +254 -151
- package/docs/cross-tool-guide.md +7 -7
- package/docs/developer-guide.md +8 -8
- package/docs/document-governance.md +15 -15
- package/docs/environment-management-guide.md +6 -6
- package/docs/examples/add-export-command/design.md +1 -1
- package/docs/faq.md +13 -13
- package/docs/handoff-profile-integration-guide.md +3 -3
- package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.json +7 -7
- package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.md +1 -1
- package/docs/integration-modes.md +12 -12
- package/docs/integration-philosophy.md +11 -11
- package/docs/interactive-customization/331-poc-sce-integration-checklist.md +24 -24
- package/docs/interactive-customization/README.md +43 -43
- package/docs/interactive-customization/business-mode-policy-baseline.json +33 -0
- package/docs/interactive-customization/dual-ui-mode-integration-guide.md +1 -1
- package/docs/interactive-customization/moqui-adapter-interface.md +2 -2
- package/docs/interactive-customization/moqui-copilot-integration-guide.md +1 -1
- package/docs/interactive-customization/moqui-interactive-template-playbook.md +4 -4
- package/docs/interactive-customization/phase-acceptance-evidence.md +2 -2
- package/docs/knowledge-management-guide.md +6 -6
- package/docs/manual-workflows-guide.md +4 -4
- package/docs/moqui-capability-matrix.md +3 -3
- package/docs/moqui-standard-rebuild-guide.md +8 -8
- package/docs/moqui-template-core-library-playbook.md +27 -27
- package/docs/multi-agent-coordination-guide.md +19 -19
- package/docs/multi-repo-management-guide.md +17 -17
- package/docs/quick-start-with-ai-tools.md +7 -7
- package/docs/quick-start.md +2 -2
- package/docs/release-checklist.md +4 -4
- package/docs/sce-business-mode-map.md +103 -0
- package/docs/sce-capability-matrix-e2e-example.md +94 -0
- package/docs/sce-capability-matrix-roadmap.md +48 -0
- package/docs/security-governance-default-baseline.md +12 -12
- package/docs/spec-collaboration-guide.md +3 -3
- package/docs/spec-locking-guide.md +2 -2
- package/docs/spec-workflow.md +3 -3
- package/docs/starter-kit/README.md +4 -4
- package/docs/starter-kit/handoff-manifest.starter.json +2 -2
- package/docs/starter-kit/release.workflow.sample.yml +1 -1
- package/docs/steering-strategy-guide.md +15 -15
- package/docs/team-collaboration-guide.md +69 -69
- package/docs/testing-strategy.md +2 -2
- package/docs/tools/claude-guide.md +14 -4
- package/docs/tools/cursor-guide.md +14 -14
- package/docs/tools/generic-guide.md +9 -9
- package/docs/tools/kiro-guide.md +4 -4
- package/docs/tools/vscode-guide.md +13 -13
- package/docs/tools/windsurf-guide.md +6 -6
- package/docs/troubleshooting.md +22 -22
- package/docs/upgrade-guide.md +8 -8
- package/docs/value-observability-guide.md +3 -3
- package/docs/zh/README.md +15 -0
- package/docs/zh/quick-start.md +15 -15
- package/docs/zh/release-checklist.md +3 -3
- package/docs/zh/tools/claude-guide.md +16 -6
- package/docs/zh/tools/cursor-guide.md +11 -11
- package/docs/zh/tools/generic-guide.md +13 -13
- package/docs/zh/tools/kiro-guide.md +2 -2
- package/docs/zh/tools/vscode-guide.md +11 -11
- package/docs/zh/tools/windsurf-guide.md +11 -11
- package/docs/zh/value-observability-guide.md +3 -3
- package/lib/adoption/adoption-logger.js +1 -1
- package/lib/adoption/adoption-strategy.js +28 -28
- package/lib/adoption/backup-manager.js +3 -3
- package/lib/adoption/conflict-resolver.js +2 -2
- package/lib/adoption/detection-engine.js +8 -8
- package/lib/adoption/error-formatter.js +4 -4
- package/lib/adoption/file-classifier.js +6 -6
- package/lib/adoption/progress-reporter.js +1 -1
- package/lib/adoption/smart-orchestrator.js +10 -10
- package/lib/adoption/strategy-selector.js +6 -6
- package/lib/adoption/summary-generator.js +1 -1
- package/lib/adoption/template-sync.js +8 -8
- package/lib/auto/autonomous-engine.js +7 -7
- package/lib/auto/checkpoint-manager.js +1 -1
- package/lib/auto/close-loop-runner.js +12 -12
- package/lib/auto/error-recovery-manager.js +1 -1
- package/lib/auto/goal-decomposer.js +1 -1
- package/lib/auto/moqui-recovery-sequence.js +2 -2
- package/lib/auto/progress-tracker.js +1 -1
- package/lib/auto/state-manager.js +1 -1
- package/lib/backup/backup-system.js +10 -10
- package/lib/backup/selective-backup.js +4 -4
- package/lib/collab/agent-registry.js +2 -2
- package/lib/collab/contract-manager.js +1 -1
- package/lib/collab/coordinator.js +2 -2
- package/lib/collab/dependency-manager.js +1 -1
- package/lib/collab/integration-manager.js +1 -1
- package/lib/collab/metadata-manager.js +1 -1
- package/lib/collab/multi-agent-config.js +2 -2
- package/lib/collab/spec-lifecycle-manager.js +2 -2
- package/lib/collab/visualizer.js +1 -1
- package/lib/commands/adopt.js +6 -6
- package/lib/commands/auto.js +56 -56
- package/lib/commands/collab.js +2 -2
- package/lib/commands/docs.js +3 -3
- package/lib/commands/doctor.js +1 -1
- package/lib/commands/knowledge.js +2 -2
- package/lib/commands/lock.js +1 -1
- package/lib/commands/ops.js +1 -1
- package/lib/commands/orchestrate.js +3 -3
- package/lib/commands/rollback.js +1 -1
- package/lib/commands/scene.js +135 -93
- package/lib/commands/session.js +139 -0
- package/lib/commands/spec-bootstrap.js +1 -1
- package/lib/commands/spec-gate.js +2 -2
- package/lib/commands/spec-pipeline.js +1 -1
- package/lib/commands/status.js +4 -4
- package/lib/commands/steering.js +119 -0
- package/lib/commands/value.js +1 -1
- package/lib/commands/watch.js +9 -9
- package/lib/commands/workspace-multi.js +1 -1
- package/lib/context/context-exporter.js +5 -7
- package/lib/context/prompt-generator.js +2 -2
- package/lib/environment/backup-system.js +1 -1
- package/lib/environment/environment-manager.js +2 -2
- package/lib/gitignore/gitignore-backup.js +3 -3
- package/lib/gitignore/gitignore-detector.js +13 -13
- package/lib/gitignore/gitignore-integration.js +3 -3
- package/lib/gitignore/gitignore-transformer.js +4 -4
- package/lib/gitignore/layered-rules-template.js +16 -16
- package/lib/governance/config-manager.js +1 -1
- package/lib/governance/doc-reference-checker.js +4 -4
- package/lib/governance/execution-logger.js +1 -1
- package/lib/governance/file-scanner.js +3 -3
- package/lib/interactive-customization/moqui-interactive-adapter.js +2 -2
- package/lib/knowledge/knowledge-manager.js +1 -1
- package/lib/lock/lock-manager.js +2 -2
- package/lib/lock/steering-file-lock.js +5 -5
- package/lib/lock/task-lock-manager.js +3 -3
- package/lib/operations/audit-logger.js +1 -1
- package/lib/operations/feedback-manager.js +1 -1
- package/lib/operations/operations-manager.js +3 -3
- package/lib/operations/permission-manager.js +2 -2
- package/lib/operations/template-loader.js +1 -1
- package/lib/orchestrator/agent-spawner.js +180 -2
- package/lib/orchestrator/bootstrap-prompt-builder.js +9 -6
- package/lib/orchestrator/orchestration-engine.js +346 -2
- package/lib/orchestrator/orchestrator-config.js +2 -2
- package/lib/repo/config-manager.js +3 -3
- package/lib/repo/handlers/init-handler.js +1 -1
- package/lib/repo/repo-manager.js +2 -2
- package/lib/runtime/business-mode-resolver.js +240 -0
- package/lib/runtime/session-store.js +207 -0
- package/lib/runtime/steering-contract.js +338 -0
- package/lib/scene-runtime/audit-emitter.js +1 -1
- package/lib/scene-runtime/binding-plugin-loader.js +3 -3
- package/lib/scene-runtime/eval-bridge.js +1 -1
- package/lib/scene-runtime/index.js +1 -1
- package/lib/scene-runtime/moqui-extractor.js +1 -1
- package/lib/scene-runtime/plan-compiler.js +1 -1
- package/lib/scene-runtime/policy-gate.js +1 -1
- package/lib/scene-runtime/runtime-executor.js +1 -1
- package/lib/scene-runtime/scene-loader.js +1 -1
- package/lib/spec/bootstrap/context-collector.js +1 -1
- package/lib/spec/pipeline/stage-adapters.js +3 -3
- package/lib/spec/pipeline/state-store.js +1 -1
- package/lib/spec-gate/policy/policy-loader.js +1 -1
- package/lib/spec-gate/rules/default-rules.js +6 -6
- package/lib/steering/adoption-config.js +1 -1
- package/lib/steering/compliance-error-reporter.js +3 -3
- package/lib/steering/context-sync-manager.js +2 -2
- package/lib/steering/index.js +1 -1
- package/lib/steering/spec-steering.js +2 -2
- package/lib/steering/steering-compliance-checker.js +1 -1
- package/lib/steering/steering-loader.js +4 -5
- package/lib/steering/steering-manager.js +4 -4
- package/lib/task/task-claimer.js +5 -5
- package/lib/task/task-status-store.js +2 -2
- package/lib/templates/content-generalizer.js +1 -1
- package/lib/templates/spec-reader.js +2 -2
- package/lib/templates/template-creator.js +1 -1
- package/lib/templates/template-exporter.js +3 -3
- package/lib/templates/template-manager.js +1 -1
- package/lib/upgrade/migration-engine.js +3 -3
- package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +1 -1
- package/lib/utils/file-diff.js +6 -6
- package/lib/utils/tool-detector.js +10 -10
- package/lib/utils/validation.js +5 -5
- package/lib/value/metric-contract-loader.js +1 -1
- package/lib/version/version-manager.js +1 -1
- package/lib/watch/execution-logger.js +1 -1
- package/lib/watch/presets.js +8 -8
- package/lib/watch/watch-manager.js +2 -2
- package/lib/workspace/legacy-kiro-migrator.js +275 -0
- package/lib/workspace/multi/workspace-context-resolver.js +2 -2
- package/lib/workspace/multi/workspace-registry.js +2 -2
- package/lib/workspace/multi/workspace-state-manager.js +3 -3
- package/lib/workspace/workspace-manager.js +1 -1
- package/lib/workspace/workspace-sync.js +2 -2
- package/locales/en.json +4 -4
- package/locales/zh.json +4 -4
- package/package.json +12 -9
- package/template/{.kiro → .sce}/README.md +15 -15
- package/template/{.kiro → .sce}/hooks/check-spec-on-create.kiro.hook +2 -2
- package/template/{.kiro → .sce}/steering/CORE_PRINCIPLES.md +4 -4
- package/template/{.kiro → .sce}/steering/CURRENT_CONTEXT.md +1 -1
- package/template/{.kiro → .sce}/steering/ENVIRONMENT.md +3 -3
- package/template/{.kiro → .sce}/tools/backup_manager.py +3 -3
- package/template/{.kiro → .sce}/tools/configuration_manager.py +1 -1
- package/template/README.md +12 -12
- /package/template/{.kiro → .sce}/hooks/run-tests-on-save.kiro.hook +0 -0
- /package/template/{.kiro → .sce}/hooks/sync-tasks-on-edit.kiro.hook +0 -0
- /package/template/{.kiro → .sce}/specs/SPEC_WORKFLOW_GUIDE.md +0 -0
- /package/template/{.kiro → .sce}/steering/RULES_GUIDE.md +0 -0
- /package/template/{.kiro → .sce}/templates/operations/default/change-impact.md +0 -0
- /package/template/{.kiro → .sce}/templates/operations/default/deployment.md +0 -0
- /package/template/{.kiro → .sce}/templates/operations/default/feedback-response.md +0 -0
- /package/template/{.kiro → .sce}/templates/operations/default/migration-plan.md +0 -0
- /package/template/{.kiro → .sce}/templates/operations/default/monitoring.md +0 -0
- /package/template/{.kiro → .sce}/templates/operations/default/operations.md +0 -0
- /package/template/{.kiro → .sce}/templates/operations/default/rollback.md +0 -0
- /package/template/{.kiro → .sce}/templates/operations/default/tools.yaml +0 -0
- /package/template/{.kiro → .sce}/templates/operations/default/troubleshooting.md +0 -0
- /package/template/{.kiro → .sce}/tools/document_evaluator.py +0 -0
- /package/template/{.kiro → .sce}/tools/enhancement_logger.py +0 -0
- /package/template/{.kiro → .sce}/tools/error_handler.py +0 -0
- /package/template/{.kiro → .sce}/tools/improvement_identifier.py +0 -0
- /package/template/{.kiro → .sce}/tools/modification_applicator.py +0 -0
- /package/template/{.kiro → .sce}/tools/quality_gate_enforcer.py +0 -0
- /package/template/{.kiro → .sce}/tools/quality_scorer.py +0 -0
- /package/template/{.kiro → .sce}/tools/report_generator.py +0 -0
- /package/template/{.kiro → .sce}/tools/ultrawork_enhancer.py +0 -0
- /package/template/{.kiro → .sce}/tools/ultrawork_enhancer_refactored.py +0 -0
- /package/template/{.kiro → .sce}/tools/ultrawork_enhancer_v2.py +0 -0
- /package/template/{.kiro → .sce}/tools/ultrawork_enhancer_v3.py +0 -0
- /package/template/{.kiro → .sce}/tools/workflow_quality_gate.py +0 -0
|
@@ -15,7 +15,7 @@ const { EventEmitter } = require('events');
|
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const fsUtils = require('../utils/fs-utils');
|
|
17
17
|
|
|
18
|
-
const SPECS_DIR = '.
|
|
18
|
+
const SPECS_DIR = '.sce/specs';
|
|
19
19
|
const DEFAULT_RATE_LIMIT_MAX_RETRIES = 8;
|
|
20
20
|
const DEFAULT_RATE_LIMIT_BACKOFF_BASE_MS = 1500;
|
|
21
21
|
const DEFAULT_RATE_LIMIT_BACKOFF_MAX_MS = 60000;
|
|
@@ -39,6 +39,25 @@ const RATE_LIMIT_ERROR_PATTERNS = [
|
|
|
39
39
|
/requests per minute/i,
|
|
40
40
|
/tokens per minute/i,
|
|
41
41
|
];
|
|
42
|
+
const DEFAULT_COORDINATION_POLICY_RELATIVE_PATH = path.join(
|
|
43
|
+
'docs',
|
|
44
|
+
'agent-runtime',
|
|
45
|
+
'multi-agent-coordination-policy-baseline.json'
|
|
46
|
+
);
|
|
47
|
+
const DEFAULT_RESULT_SUMMARY_REQUIRED_FIELDS = [
|
|
48
|
+
'spec_id',
|
|
49
|
+
'changed_files',
|
|
50
|
+
'tests_run',
|
|
51
|
+
'tests_passed',
|
|
52
|
+
'risk_level',
|
|
53
|
+
'open_issues'
|
|
54
|
+
];
|
|
55
|
+
const DEFAULT_COORDINATION_RULES = {
|
|
56
|
+
require_result_summary: false,
|
|
57
|
+
block_merge_on_failed_tests: true,
|
|
58
|
+
block_merge_on_unresolved_conflicts: true
|
|
59
|
+
};
|
|
60
|
+
const VALID_RESULT_RISK_LEVELS = new Set(['low', 'medium', 'high', 'unknown']);
|
|
42
61
|
|
|
43
62
|
class OrchestrationEngine extends EventEmitter {
|
|
44
63
|
/**
|
|
@@ -115,6 +134,14 @@ class OrchestrationEngine extends EventEmitter {
|
|
|
115
134
|
this._random = typeof options.random === 'function' ? options.random : Math.random;
|
|
116
135
|
/** @type {() => number} */
|
|
117
136
|
this._now = typeof options.now === 'function' ? options.now : Date.now;
|
|
137
|
+
/** @type {{ require_result_summary: boolean, block_merge_on_failed_tests: boolean, block_merge_on_unresolved_conflicts: boolean }} */
|
|
138
|
+
this._coordinationRules = { ...DEFAULT_COORDINATION_RULES };
|
|
139
|
+
/** @type {string[]} */
|
|
140
|
+
this._resultSummaryRequiredFields = [...DEFAULT_RESULT_SUMMARY_REQUIRED_FIELDS];
|
|
141
|
+
/** @type {Map<string, object>} */
|
|
142
|
+
this._resultSummaries = new Map();
|
|
143
|
+
/** @type {string} */
|
|
144
|
+
this._coordinationPolicyPath = DEFAULT_COORDINATION_POLICY_RELATIVE_PATH;
|
|
118
145
|
}
|
|
119
146
|
|
|
120
147
|
// ---------------------------------------------------------------------------
|
|
@@ -197,6 +224,7 @@ class OrchestrationEngine extends EventEmitter {
|
|
|
197
224
|
// Get config for maxParallel and maxRetries
|
|
198
225
|
const config = await this._orchestratorConfig.getConfig();
|
|
199
226
|
this._applyRetryPolicyConfig(config);
|
|
227
|
+
await this._applyCoordinationPolicyConfig(config);
|
|
200
228
|
this._agentWaitTimeoutMs = this._resolveAgentWaitTimeoutMs(config);
|
|
201
229
|
const maxParallel = options.maxParallel || config.maxParallel || 3;
|
|
202
230
|
const maxRetries = config.maxRetries || 2;
|
|
@@ -486,6 +514,33 @@ class OrchestrationEngine extends EventEmitter {
|
|
|
486
514
|
* @private
|
|
487
515
|
*/
|
|
488
516
|
async _handleSpecCompleted(specName, agentId) {
|
|
517
|
+
const summaryValidation = this._resolveAndValidateResultSummary(specName, agentId);
|
|
518
|
+
if (!summaryValidation.valid) {
|
|
519
|
+
await this._handleSummaryContractViolation(
|
|
520
|
+
specName,
|
|
521
|
+
agentId,
|
|
522
|
+
summaryValidation.message,
|
|
523
|
+
summaryValidation
|
|
524
|
+
);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const mergeDecision = this._evaluateMergeDecision(summaryValidation.summary);
|
|
529
|
+
if (!mergeDecision.allowed) {
|
|
530
|
+
await this._handleSummaryContractViolation(
|
|
531
|
+
specName,
|
|
532
|
+
agentId,
|
|
533
|
+
`merge blocked by result summary policy: ${mergeDecision.reasons.join('; ')}`,
|
|
534
|
+
{
|
|
535
|
+
valid: true,
|
|
536
|
+
summary: summaryValidation.summary,
|
|
537
|
+
issues: mergeDecision.reasons
|
|
538
|
+
}
|
|
539
|
+
);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
this._resultSummaries.set(specName, { ...summaryValidation.summary });
|
|
489
544
|
this._completedSpecs.add(specName);
|
|
490
545
|
this._statusMonitor.updateSpecStatus(specName, 'completed', agentId);
|
|
491
546
|
|
|
@@ -495,7 +550,12 @@ class OrchestrationEngine extends EventEmitter {
|
|
|
495
550
|
// Sync external status (Req 8.5)
|
|
496
551
|
await this._syncExternalSafe(specName, 'completed');
|
|
497
552
|
|
|
498
|
-
this.emit('spec:complete', {
|
|
553
|
+
this.emit('spec:complete', {
|
|
554
|
+
specName,
|
|
555
|
+
agentId,
|
|
556
|
+
result_summary: summaryValidation.summary,
|
|
557
|
+
merge_decision: 'accepted'
|
|
558
|
+
});
|
|
499
559
|
}
|
|
500
560
|
|
|
501
561
|
/**
|
|
@@ -673,6 +733,277 @@ class OrchestrationEngine extends EventEmitter {
|
|
|
673
733
|
}
|
|
674
734
|
}
|
|
675
735
|
|
|
736
|
+
/**
|
|
737
|
+
* Resolve coordination policy from baseline file and runtime config.
|
|
738
|
+
*
|
|
739
|
+
* @param {object} config
|
|
740
|
+
* @returns {Promise<void>}
|
|
741
|
+
* @private
|
|
742
|
+
*/
|
|
743
|
+
async _applyCoordinationPolicyConfig(config) {
|
|
744
|
+
const baseline = await this._loadCoordinationPolicyBaseline(config);
|
|
745
|
+
const baselineRules = baseline && baseline.coordination_rules
|
|
746
|
+
? baseline.coordination_rules
|
|
747
|
+
: {};
|
|
748
|
+
const runtimeRules = config && config.coordinationRules && typeof config.coordinationRules === 'object'
|
|
749
|
+
? config.coordinationRules
|
|
750
|
+
: {};
|
|
751
|
+
const requireFields = baseline
|
|
752
|
+
&& baseline.result_summary_contract
|
|
753
|
+
&& Array.isArray(baseline.result_summary_contract.required_fields)
|
|
754
|
+
? baseline.result_summary_contract.required_fields
|
|
755
|
+
: DEFAULT_RESULT_SUMMARY_REQUIRED_FIELDS;
|
|
756
|
+
const overrideFields = Array.isArray(config && config.resultSummaryRequiredFields)
|
|
757
|
+
? config.resultSummaryRequiredFields
|
|
758
|
+
: null;
|
|
759
|
+
|
|
760
|
+
this._coordinationRules = {
|
|
761
|
+
require_result_summary: this._toBoolean(
|
|
762
|
+
runtimeRules.require_result_summary,
|
|
763
|
+
this._toBoolean(baselineRules.require_result_summary, DEFAULT_COORDINATION_RULES.require_result_summary)
|
|
764
|
+
),
|
|
765
|
+
block_merge_on_failed_tests: this._toBoolean(
|
|
766
|
+
runtimeRules.block_merge_on_failed_tests,
|
|
767
|
+
this._toBoolean(
|
|
768
|
+
baselineRules.block_merge_on_failed_tests,
|
|
769
|
+
DEFAULT_COORDINATION_RULES.block_merge_on_failed_tests
|
|
770
|
+
)
|
|
771
|
+
),
|
|
772
|
+
block_merge_on_unresolved_conflicts: this._toBoolean(
|
|
773
|
+
runtimeRules.block_merge_on_unresolved_conflicts,
|
|
774
|
+
this._toBoolean(
|
|
775
|
+
baselineRules.block_merge_on_unresolved_conflicts,
|
|
776
|
+
DEFAULT_COORDINATION_RULES.block_merge_on_unresolved_conflicts
|
|
777
|
+
)
|
|
778
|
+
)
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
const selectedFields = overrideFields || requireFields;
|
|
782
|
+
const normalizedFields = selectedFields
|
|
783
|
+
.map((field) => `${field || ''}`.trim())
|
|
784
|
+
.filter(Boolean);
|
|
785
|
+
this._resultSummaryRequiredFields = normalizedFields.length > 0
|
|
786
|
+
? normalizedFields
|
|
787
|
+
: [...DEFAULT_RESULT_SUMMARY_REQUIRED_FIELDS];
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* @param {object} config
|
|
792
|
+
* @returns {Promise<object>}
|
|
793
|
+
* @private
|
|
794
|
+
*/
|
|
795
|
+
async _loadCoordinationPolicyBaseline(config) {
|
|
796
|
+
const relativePath = (
|
|
797
|
+
config
|
|
798
|
+
&& typeof config.coordinationPolicyFile === 'string'
|
|
799
|
+
&& config.coordinationPolicyFile.trim()
|
|
800
|
+
)
|
|
801
|
+
? config.coordinationPolicyFile.trim()
|
|
802
|
+
: this._coordinationPolicyPath;
|
|
803
|
+
const policyPath = path.resolve(this._workspaceRoot, relativePath);
|
|
804
|
+
const exists = await fsUtils.pathExists(policyPath);
|
|
805
|
+
if (!exists) {
|
|
806
|
+
return {};
|
|
807
|
+
}
|
|
808
|
+
try {
|
|
809
|
+
return await fsUtils.readJSON(policyPath);
|
|
810
|
+
} catch (err) {
|
|
811
|
+
console.warn(`[OrchestrationEngine] Failed to parse coordination policy: ${err.message}`);
|
|
812
|
+
return {};
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* @param {string} specName
|
|
818
|
+
* @param {string} agentId
|
|
819
|
+
* @returns {{ valid: boolean, summary: object|null, issues: string[], message: string }}
|
|
820
|
+
* @private
|
|
821
|
+
*/
|
|
822
|
+
_resolveAndValidateResultSummary(specName, agentId) {
|
|
823
|
+
const requireSummary = this._coordinationRules
|
|
824
|
+
&& this._coordinationRules.require_result_summary === true;
|
|
825
|
+
if (!requireSummary) {
|
|
826
|
+
return {
|
|
827
|
+
valid: true,
|
|
828
|
+
summary: {
|
|
829
|
+
spec_id: specName,
|
|
830
|
+
changed_files: [],
|
|
831
|
+
tests_run: 0,
|
|
832
|
+
tests_passed: 0,
|
|
833
|
+
risk_level: 'unknown',
|
|
834
|
+
open_issues: []
|
|
835
|
+
},
|
|
836
|
+
issues: [],
|
|
837
|
+
message: ''
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
const summary = this._readResultSummaryFromSpawner(agentId);
|
|
842
|
+
if (!summary) {
|
|
843
|
+
return {
|
|
844
|
+
valid: false,
|
|
845
|
+
summary: null,
|
|
846
|
+
issues: ['missing result summary payload'],
|
|
847
|
+
message: 'result summary contract missing'
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const normalizedSummary = this._normalizeResultSummary(summary, specName);
|
|
852
|
+
const issues = this._validateResultSummary(normalizedSummary);
|
|
853
|
+
return {
|
|
854
|
+
valid: issues.length === 0,
|
|
855
|
+
summary: normalizedSummary,
|
|
856
|
+
issues,
|
|
857
|
+
message: issues.length === 0 ? '' : `result summary contract invalid: ${issues.join('; ')}`
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* @param {string} agentId
|
|
863
|
+
* @returns {object|null}
|
|
864
|
+
* @private
|
|
865
|
+
*/
|
|
866
|
+
_readResultSummaryFromSpawner(agentId) {
|
|
867
|
+
if (!this._agentSpawner || typeof this._agentSpawner.getResultSummary !== 'function') {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
try {
|
|
871
|
+
return this._agentSpawner.getResultSummary(agentId);
|
|
872
|
+
} catch (_err) {
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* @param {object} summary
|
|
879
|
+
* @param {string} fallbackSpecName
|
|
880
|
+
* @returns {object}
|
|
881
|
+
* @private
|
|
882
|
+
*/
|
|
883
|
+
_normalizeResultSummary(summary, fallbackSpecName) {
|
|
884
|
+
const changedFiles = Array.isArray(summary.changed_files)
|
|
885
|
+
? summary.changed_files.map((item) => `${item || ''}`.trim()).filter(Boolean)
|
|
886
|
+
: [];
|
|
887
|
+
const openIssues = Array.isArray(summary.open_issues)
|
|
888
|
+
? summary.open_issues.map((item) => `${item || ''}`.trim()).filter(Boolean)
|
|
889
|
+
: [];
|
|
890
|
+
const testsRun = Number(summary.tests_run);
|
|
891
|
+
const testsPassed = Number(summary.tests_passed);
|
|
892
|
+
const normalizedRisk = `${summary.risk_level || 'unknown'}`.trim().toLowerCase();
|
|
893
|
+
|
|
894
|
+
return {
|
|
895
|
+
spec_id: `${summary.spec_id || fallbackSpecName || ''}`.trim(),
|
|
896
|
+
changed_files: changedFiles,
|
|
897
|
+
tests_run: Number.isFinite(testsRun) ? Math.max(0, Math.floor(testsRun)) : NaN,
|
|
898
|
+
tests_passed: Number.isFinite(testsPassed) ? Math.max(0, Math.floor(testsPassed)) : NaN,
|
|
899
|
+
risk_level: normalizedRisk || 'unknown',
|
|
900
|
+
open_issues: openIssues
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* @param {object} summary
|
|
906
|
+
* @returns {string[]}
|
|
907
|
+
* @private
|
|
908
|
+
*/
|
|
909
|
+
_validateResultSummary(summary) {
|
|
910
|
+
const issues = [];
|
|
911
|
+
const requiredFields = Array.isArray(this._resultSummaryRequiredFields)
|
|
912
|
+
? this._resultSummaryRequiredFields
|
|
913
|
+
: [];
|
|
914
|
+
for (const field of requiredFields) {
|
|
915
|
+
if (!Object.prototype.hasOwnProperty.call(summary, field)) {
|
|
916
|
+
issues.push(`missing field '${field}'`);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if (!summary.spec_id) {
|
|
921
|
+
issues.push('spec_id must be non-empty');
|
|
922
|
+
}
|
|
923
|
+
if (!Array.isArray(summary.changed_files)) {
|
|
924
|
+
issues.push('changed_files must be an array');
|
|
925
|
+
}
|
|
926
|
+
if (!Number.isFinite(summary.tests_run)) {
|
|
927
|
+
issues.push('tests_run must be a non-negative integer');
|
|
928
|
+
}
|
|
929
|
+
if (!Number.isFinite(summary.tests_passed)) {
|
|
930
|
+
issues.push('tests_passed must be a non-negative integer');
|
|
931
|
+
}
|
|
932
|
+
if (
|
|
933
|
+
Number.isFinite(summary.tests_run)
|
|
934
|
+
&& Number.isFinite(summary.tests_passed)
|
|
935
|
+
&& summary.tests_passed > summary.tests_run
|
|
936
|
+
) {
|
|
937
|
+
issues.push('tests_passed cannot exceed tests_run');
|
|
938
|
+
}
|
|
939
|
+
if (!VALID_RESULT_RISK_LEVELS.has(summary.risk_level)) {
|
|
940
|
+
issues.push(`risk_level must be one of: ${[...VALID_RESULT_RISK_LEVELS].join(', ')}`);
|
|
941
|
+
}
|
|
942
|
+
if (!Array.isArray(summary.open_issues)) {
|
|
943
|
+
issues.push('open_issues must be an array');
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
return issues;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* @param {object} summary
|
|
951
|
+
* @returns {{ allowed: boolean, reasons: string[] }}
|
|
952
|
+
* @private
|
|
953
|
+
*/
|
|
954
|
+
_evaluateMergeDecision(summary) {
|
|
955
|
+
const reasons = [];
|
|
956
|
+
const coordinationRules = this._coordinationRules || DEFAULT_COORDINATION_RULES;
|
|
957
|
+
if (
|
|
958
|
+
coordinationRules.block_merge_on_failed_tests
|
|
959
|
+
&& Number.isFinite(summary.tests_run)
|
|
960
|
+
&& Number.isFinite(summary.tests_passed)
|
|
961
|
+
&& summary.tests_run > summary.tests_passed
|
|
962
|
+
) {
|
|
963
|
+
reasons.push(
|
|
964
|
+
`tests failed (${summary.tests_passed}/${summary.tests_run} passed)`
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
if (coordinationRules.block_merge_on_unresolved_conflicts) {
|
|
969
|
+
const hasConflictIssue = Array.isArray(summary.open_issues)
|
|
970
|
+
&& summary.open_issues.some((issue) => /conflict|unresolved/i.test(`${issue}`));
|
|
971
|
+
if (hasConflictIssue) {
|
|
972
|
+
reasons.push('open issues contain unresolved conflict');
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
return {
|
|
977
|
+
allowed: reasons.length === 0,
|
|
978
|
+
reasons
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* @param {string} specName
|
|
984
|
+
* @param {string} agentId
|
|
985
|
+
* @param {string} errorMessage
|
|
986
|
+
* @param {object} validation
|
|
987
|
+
* @returns {Promise<void>}
|
|
988
|
+
* @private
|
|
989
|
+
*/
|
|
990
|
+
async _handleSummaryContractViolation(specName, agentId, errorMessage, validation = {}) {
|
|
991
|
+
this._completedSpecs.delete(specName);
|
|
992
|
+
this._failedSpecs.add(specName);
|
|
993
|
+
this._statusMonitor.updateSpecStatus(specName, 'failed', agentId, errorMessage);
|
|
994
|
+
await this._syncExternalSafe(specName, 'failed');
|
|
995
|
+
|
|
996
|
+
this.emit('spec:failed', {
|
|
997
|
+
specName,
|
|
998
|
+
agentId,
|
|
999
|
+
error: errorMessage,
|
|
1000
|
+
summary_contract_violation: true,
|
|
1001
|
+
summary_validation: validation
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
this._propagateFailure(specName);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
676
1007
|
// ---------------------------------------------------------------------------
|
|
677
1008
|
// Validation & Helpers
|
|
678
1009
|
// ---------------------------------------------------------------------------
|
|
@@ -1342,12 +1673,20 @@ class OrchestrationEngine extends EventEmitter {
|
|
|
1342
1673
|
* @private
|
|
1343
1674
|
*/
|
|
1344
1675
|
_buildResult(status, error = null) {
|
|
1676
|
+
const resultSummaries = {};
|
|
1677
|
+
if (this._resultSummaries && typeof this._resultSummaries.entries === 'function') {
|
|
1678
|
+
for (const [specName, summary] of this._resultSummaries.entries()) {
|
|
1679
|
+
resultSummaries[specName] = { ...summary };
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1345
1682
|
return {
|
|
1346
1683
|
status,
|
|
1347
1684
|
plan: this._executionPlan,
|
|
1348
1685
|
completed: [...this._completedSpecs],
|
|
1349
1686
|
failed: [...this._failedSpecs],
|
|
1350
1687
|
skipped: [...this._skippedSpecs],
|
|
1688
|
+
result_summaries: resultSummaries,
|
|
1689
|
+
coordination_rules: { ...(this._coordinationRules || DEFAULT_COORDINATION_RULES) },
|
|
1351
1690
|
error,
|
|
1352
1691
|
};
|
|
1353
1692
|
}
|
|
@@ -1363,6 +1702,11 @@ class OrchestrationEngine extends EventEmitter {
|
|
|
1363
1702
|
this._failedSpecs.clear();
|
|
1364
1703
|
this._skippedSpecs.clear();
|
|
1365
1704
|
this._completedSpecs.clear();
|
|
1705
|
+
if (this._resultSummaries && typeof this._resultSummaries.clear === 'function') {
|
|
1706
|
+
this._resultSummaries.clear();
|
|
1707
|
+
} else {
|
|
1708
|
+
this._resultSummaries = new Map();
|
|
1709
|
+
}
|
|
1366
1710
|
this._executionPlan = null;
|
|
1367
1711
|
this._stopped = false;
|
|
1368
1712
|
this._baseMaxParallel = null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Orchestrator Configuration Manager
|
|
3
3
|
*
|
|
4
|
-
* Manages `.
|
|
4
|
+
* Manages `.sce/config/orchestrator.json` for the Agent Orchestrator.
|
|
5
5
|
* When the config file does not exist or contains invalid JSON,
|
|
6
6
|
* returns a default configuration so that orchestration can proceed
|
|
7
7
|
* with sensible defaults.
|
|
@@ -16,7 +16,7 @@ const fs = require('fs-extra');
|
|
|
16
16
|
const fsUtils = require('../utils/fs-utils');
|
|
17
17
|
|
|
18
18
|
const CONFIG_FILENAME = 'orchestrator.json';
|
|
19
|
-
const CONFIG_DIR = '.
|
|
19
|
+
const CONFIG_DIR = '.sce/config';
|
|
20
20
|
|
|
21
21
|
/** Known configuration keys — anything else is silently ignored. */
|
|
22
22
|
const KNOWN_KEYS = new Set([
|
|
@@ -28,7 +28,7 @@ class ConfigManager {
|
|
|
28
28
|
* @returns {string} Absolute path to the configuration file
|
|
29
29
|
*/
|
|
30
30
|
getConfigPath() {
|
|
31
|
-
return path.join(this.projectRoot, '.
|
|
31
|
+
return path.join(this.projectRoot, '.sce', this.configFileName);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -130,7 +130,7 @@ class ConfigManager {
|
|
|
130
130
|
const configPath = this.getConfigPath();
|
|
131
131
|
|
|
132
132
|
try {
|
|
133
|
-
// Ensure .
|
|
133
|
+
// Ensure .sce directory exists
|
|
134
134
|
const kiroDir = path.dirname(configPath);
|
|
135
135
|
await fs.mkdir(kiroDir, { recursive: true });
|
|
136
136
|
|
|
@@ -471,7 +471,7 @@ class ConfigManager {
|
|
|
471
471
|
_isValidRepoName(name) {
|
|
472
472
|
// Allow alphanumeric, hyphens, underscores, dots, and names starting with dots (hidden directories)
|
|
473
473
|
// Must not contain spaces or special characters like @, #, $, etc.
|
|
474
|
-
// Examples: "backend", ".github", ".
|
|
474
|
+
// Examples: "backend", ".github", ".sce", "my-repo", "repo_name", "repo.name"
|
|
475
475
|
const validPattern = /^\.?[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
476
476
|
return validPattern.test(name);
|
|
477
477
|
}
|
|
@@ -31,7 +31,7 @@ class InitHandler {
|
|
|
31
31
|
* @returns {Promise<Object>} Initialization result
|
|
32
32
|
*/
|
|
33
33
|
async execute(options = {}) {
|
|
34
|
-
const { yes = false, maxDepth = 3, exclude = ['.
|
|
34
|
+
const { yes = false, maxDepth = 3, exclude = ['.sce'], nested = true } = options;
|
|
35
35
|
|
|
36
36
|
// Check if configuration already exists
|
|
37
37
|
const configExists = await this.configManager.configExists();
|
package/lib/repo/repo-manager.js
CHANGED
|
@@ -29,13 +29,13 @@ class RepoManager {
|
|
|
29
29
|
* @param {string} rootPath - Root path to scan
|
|
30
30
|
* @param {Object} options - Scan options
|
|
31
31
|
* @param {number} options.maxDepth - Maximum depth to scan (default: 3)
|
|
32
|
-
* @param {string[]} options.exclude - Directories to exclude (default: ['.
|
|
32
|
+
* @param {string[]} options.exclude - Directories to exclude (default: ['.sce'])
|
|
33
33
|
* @param {boolean} options.nested - Enable nested repository scanning (default: true)
|
|
34
34
|
* @returns {Promise<Array<{path: string, name: string, remote: string|null, branch: string, hasRemote: boolean, parent: string|null}>>}
|
|
35
35
|
*/
|
|
36
36
|
async discoverRepositories(rootPath, options = {}) {
|
|
37
37
|
const maxDepth = options.maxDepth || 3;
|
|
38
|
-
const exclude = options.exclude || ['.
|
|
38
|
+
const exclude = options.exclude || ['.sce'];
|
|
39
39
|
const nested = options.nested !== false; // Default to true
|
|
40
40
|
|
|
41
41
|
const discovered = [];
|