@hongmaple0820/scale-engine 0.21.2 → 0.23.0
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/README.en.md +35 -11
- package/README.md +54 -23
- package/dist/api/cli.js +284 -4
- package/dist/api/cli.js.map +1 -1
- package/dist/api/doctor.d.ts +1 -0
- package/dist/api/doctor.js +83 -0
- package/dist/api/doctor.js.map +1 -1
- package/dist/artifact/types.d.ts +1 -1
- package/dist/artifact/types.js.map +1 -1
- package/dist/cli/phaseCommands.js +22 -6
- package/dist/cli/phaseCommands.js.map +1 -1
- package/dist/cli/runCommand.d.ts +39 -0
- package/dist/cli/runCommand.js +113 -0
- package/dist/cli/runCommand.js.map +1 -0
- package/dist/config/profiles.d.ts +52 -0
- package/dist/config/profiles.js +162 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/context/ContextBudget.d.ts +25 -1
- package/dist/context/ContextBudget.js +72 -7
- package/dist/context/ContextBudget.js.map +1 -1
- package/dist/context/ProjectAnatomy.d.ts +18 -0
- package/dist/context/ProjectAnatomy.js +287 -0
- package/dist/context/ProjectAnatomy.js.map +1 -0
- package/dist/dashboard/DashboardServer.d.ts +3 -0
- package/dist/dashboard/DashboardServer.js +114 -0
- package/dist/dashboard/DashboardServer.js.map +1 -1
- package/dist/hooks/BugPatternDetector.d.ts +36 -0
- package/dist/hooks/BugPatternDetector.js +207 -0
- package/dist/hooks/BugPatternDetector.js.map +1 -0
- package/dist/hooks/HookGeneratorEnhanced.js +301 -5
- package/dist/hooks/HookGeneratorEnhanced.js.map +1 -1
- package/dist/hooks/WorkflowHooksManager.js +24 -0
- package/dist/hooks/WorkflowHooksManager.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/knowledge/CerebrumManager.d.ts +25 -0
- package/dist/knowledge/CerebrumManager.js +127 -0
- package/dist/knowledge/CerebrumManager.js.map +1 -0
- package/dist/knowledge/SQLiteKnowledgeBase.d.ts +1 -0
- package/dist/knowledge/SQLiteKnowledgeBase.js +31 -3
- package/dist/knowledge/SQLiteKnowledgeBase.js.map +1 -1
- package/dist/knowledge/TfidfIndex.d.ts +50 -0
- package/dist/knowledge/TfidfIndex.js +177 -0
- package/dist/knowledge/TfidfIndex.js.map +1 -0
- package/dist/skills/SkillDiscovery.js +1 -1
- package/dist/skills/SkillDiscovery.js.map +1 -1
- package/dist/tools/CommandOutputCompressor.d.ts +28 -0
- package/dist/tools/CommandOutputCompressor.js +242 -0
- package/dist/tools/CommandOutputCompressor.js.map +1 -0
- package/dist/tools/CommandRunLedger.d.ts +77 -0
- package/dist/tools/CommandRunLedger.js +111 -0
- package/dist/tools/CommandRunLedger.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/workflow/EngineeringStandards.js +91 -2
- package/dist/workflow/EngineeringStandards.js.map +1 -1
- package/dist/workflow/GovernanceTemplatePacks.js +2 -2
- package/dist/workflow/GovernanceTemplates.js +93 -92
- package/dist/workflow/GovernanceTemplates.js.map +1 -1
- package/dist/workflow/TaskLevelDetector.d.ts +41 -0
- package/dist/workflow/TaskLevelDetector.js +219 -0
- package/dist/workflow/TaskLevelDetector.js.map +1 -0
- package/dist/workflow/WorkflowOrchestrator.d.ts +59 -0
- package/dist/workflow/WorkflowOrchestrator.js +326 -0
- package/dist/workflow/WorkflowOrchestrator.js.map +1 -0
- package/dist/workflow/gates/GateSystem.d.ts +23 -6
- package/dist/workflow/gates/GateSystem.js +114 -25
- package/dist/workflow/gates/GateSystem.js.map +1 -1
- package/dist/workflow/gates/MetaGovernanceGates.d.ts +70 -0
- package/dist/workflow/gates/MetaGovernanceGates.js +617 -0
- package/dist/workflow/gates/MetaGovernanceGates.js.map +1 -0
- package/dist/workflow/types.d.ts +6 -1
- package/docs/README.md +2 -0
- package/docs/start/README.md +29 -3
- package/docs/start/artifact-lifecycle.md +326 -0
- package/docs/start/workflow-upgrade.md +150 -0
- package/image/wechat-public.jpg +0 -0
- package/image/wxPay.jpg +0 -0
- package/image/zfb.jpg +0 -0
- package/package.json +10 -9
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type TaskLevel = 'S' | 'M' | 'L' | 'CRITICAL';
|
|
2
|
+
export interface TaskLevelSignals {
|
|
3
|
+
fileCount: number;
|
|
4
|
+
lineDelta: number;
|
|
5
|
+
crossModule: boolean;
|
|
6
|
+
criticalFileHits: string[];
|
|
7
|
+
descriptionKeywords: string[];
|
|
8
|
+
topDirs: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface TaskLevelResult {
|
|
11
|
+
level: TaskLevel;
|
|
12
|
+
confidence: number;
|
|
13
|
+
reasons: string[];
|
|
14
|
+
signals: TaskLevelSignals;
|
|
15
|
+
}
|
|
16
|
+
export declare class TaskLevelDetector {
|
|
17
|
+
/**
|
|
18
|
+
* 从 git diff 采集信号并判定等级
|
|
19
|
+
*/
|
|
20
|
+
detectFromGitDiff(cwd?: string): Promise<TaskLevelResult>;
|
|
21
|
+
/**
|
|
22
|
+
* 从任务描述 + 文件列表判定等级
|
|
23
|
+
*/
|
|
24
|
+
detectFromDescription(description: string, files?: string[]): TaskLevelResult;
|
|
25
|
+
/**
|
|
26
|
+
* 综合判定任务等级
|
|
27
|
+
*/
|
|
28
|
+
classify(signals: TaskLevelSignals): TaskLevelResult;
|
|
29
|
+
/**
|
|
30
|
+
* 从 git diff 采集信号
|
|
31
|
+
*/
|
|
32
|
+
private collectGitSignals;
|
|
33
|
+
/**
|
|
34
|
+
* 从描述 + 文件列表采集信号
|
|
35
|
+
*/
|
|
36
|
+
private collectDescriptionSignals;
|
|
37
|
+
/**
|
|
38
|
+
* 构建信号对象
|
|
39
|
+
*/
|
|
40
|
+
private buildSignals;
|
|
41
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// SCALE Engine — Task Level Detector
|
|
2
|
+
// 自动推断任务等级 (S/M/L/CRITICAL),基于代码变更规模 + 项目结构复杂度
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
const CRITICAL_KEYWORDS = ['migration', 'migrate', 'auth', 'payment', 'pay', 'security', 'secret', 'credential', 'drop', 'alter', 'truncate', 'production', 'prod'];
|
|
5
|
+
const L_KEYWORDS = ['refactor', 'redesign', 'restructure', 'rewrite', 'architecture', 'schema', 'database', 'infrastructure'];
|
|
6
|
+
const S_KEYWORDS = ['typo', 'comment', 'readme', 'doc', 'format', 'whitespace', 'rename', 'lint'];
|
|
7
|
+
const CRITICAL_FILES = [
|
|
8
|
+
/migration/i, /schema/i, /\.env/, /docker-compose/i, /Dockerfile/i,
|
|
9
|
+
/auth/i, /payment/i, /security/i, /credential/i,
|
|
10
|
+
/package\.json$/, /pom\.xml$/, /build\.gradle$/,
|
|
11
|
+
];
|
|
12
|
+
export class TaskLevelDetector {
|
|
13
|
+
/**
|
|
14
|
+
* 从 git diff 采集信号并判定等级
|
|
15
|
+
*/
|
|
16
|
+
async detectFromGitDiff(cwd) {
|
|
17
|
+
const signals = await this.collectGitSignals(cwd);
|
|
18
|
+
return this.classify(signals);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 从任务描述 + 文件列表判定等级
|
|
22
|
+
*/
|
|
23
|
+
detectFromDescription(description, files = []) {
|
|
24
|
+
const signals = this.collectDescriptionSignals(description, files);
|
|
25
|
+
return this.classify(signals);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 综合判定任务等级
|
|
29
|
+
*/
|
|
30
|
+
classify(signals) {
|
|
31
|
+
const reasons = [];
|
|
32
|
+
let score = 0;
|
|
33
|
+
// CRITICAL: 关键词命中
|
|
34
|
+
if (signals.descriptionKeywords.some(kw => CRITICAL_KEYWORDS.includes(kw))) {
|
|
35
|
+
score += 100;
|
|
36
|
+
reasons.push('CRITICAL keyword detected in description');
|
|
37
|
+
}
|
|
38
|
+
// CRITICAL: 关键文件命中
|
|
39
|
+
if (signals.criticalFileHits.length > 0) {
|
|
40
|
+
score += 80;
|
|
41
|
+
reasons.push(`Critical files touched: ${signals.criticalFileHits.join(', ')}`);
|
|
42
|
+
}
|
|
43
|
+
// 文件数量评分
|
|
44
|
+
if (signals.fileCount > 20) {
|
|
45
|
+
score += 40;
|
|
46
|
+
reasons.push(`High file count: ${signals.fileCount}`);
|
|
47
|
+
}
|
|
48
|
+
else if (signals.fileCount > 10) {
|
|
49
|
+
score += 20;
|
|
50
|
+
reasons.push(`Medium file count: ${signals.fileCount}`);
|
|
51
|
+
}
|
|
52
|
+
else if (signals.fileCount <= 5) {
|
|
53
|
+
score -= 10;
|
|
54
|
+
reasons.push(`Low file count: ${signals.fileCount}`);
|
|
55
|
+
}
|
|
56
|
+
// 行数评分
|
|
57
|
+
if (signals.lineDelta > 500) {
|
|
58
|
+
score += 40;
|
|
59
|
+
reasons.push(`Large change: ${signals.lineDelta} lines`);
|
|
60
|
+
}
|
|
61
|
+
else if (signals.lineDelta > 100) {
|
|
62
|
+
score += 20;
|
|
63
|
+
reasons.push(`Medium change: ${signals.lineDelta} lines`);
|
|
64
|
+
}
|
|
65
|
+
else if (signals.lineDelta <= 50) {
|
|
66
|
+
score -= 10;
|
|
67
|
+
reasons.push(`Small change: ${signals.lineDelta} lines`);
|
|
68
|
+
}
|
|
69
|
+
// 跨模块评分
|
|
70
|
+
if (signals.crossModule) {
|
|
71
|
+
score += 30;
|
|
72
|
+
reasons.push(`Cross-module change: ${signals.topDirs.join(', ')}`);
|
|
73
|
+
}
|
|
74
|
+
// L 关键词
|
|
75
|
+
if (signals.descriptionKeywords.some(kw => L_KEYWORDS.includes(kw))) {
|
|
76
|
+
score += 25;
|
|
77
|
+
reasons.push('L-level keyword detected');
|
|
78
|
+
}
|
|
79
|
+
// S 关键词(降级)
|
|
80
|
+
if (signals.descriptionKeywords.some(kw => S_KEYWORDS.includes(kw))) {
|
|
81
|
+
score -= 20;
|
|
82
|
+
reasons.push('S-level keyword detected');
|
|
83
|
+
}
|
|
84
|
+
// 判定等级
|
|
85
|
+
let level;
|
|
86
|
+
let confidence;
|
|
87
|
+
if (score >= 80) {
|
|
88
|
+
level = 'CRITICAL';
|
|
89
|
+
confidence = Math.min(1, 0.7 + (score - 80) / 100);
|
|
90
|
+
}
|
|
91
|
+
else if (score >= 40) {
|
|
92
|
+
level = 'L';
|
|
93
|
+
confidence = Math.min(1, 0.6 + (score - 40) / 100);
|
|
94
|
+
}
|
|
95
|
+
else if (score >= 0) {
|
|
96
|
+
level = 'M';
|
|
97
|
+
confidence = Math.min(1, 0.5 + score / 80);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
level = 'S';
|
|
101
|
+
confidence = Math.min(1, 0.6 + Math.abs(score) / 40);
|
|
102
|
+
}
|
|
103
|
+
return { level, confidence: Math.round(confidence * 100) / 100, reasons, signals };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 从 git diff 采集信号
|
|
107
|
+
*/
|
|
108
|
+
async collectGitSignals(cwd) {
|
|
109
|
+
const workingDir = cwd ?? process.cwd();
|
|
110
|
+
let fileCount = 0;
|
|
111
|
+
let lineDelta = 0;
|
|
112
|
+
const changedFiles = [];
|
|
113
|
+
try {
|
|
114
|
+
// 获取 staged + unstaged 变更
|
|
115
|
+
const diffStat = execSync('git diff --stat HEAD --no-color 2>/dev/null || git diff --stat --no-color 2>/dev/null', {
|
|
116
|
+
cwd: workingDir,
|
|
117
|
+
encoding: 'utf-8',
|
|
118
|
+
timeout: 5000,
|
|
119
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
120
|
+
}).trim();
|
|
121
|
+
if (diffStat) {
|
|
122
|
+
const lines = diffStat.split('\n').filter(l => l.trim() && !l.includes('files changed'));
|
|
123
|
+
fileCount = lines.length;
|
|
124
|
+
for (const line of lines) {
|
|
125
|
+
// 解析: " src/foo.ts | 10 +++---"
|
|
126
|
+
const fileMatch = line.match(/^\s*(.+?)\s*\|/);
|
|
127
|
+
if (fileMatch)
|
|
128
|
+
changedFiles.push(fileMatch[1].trim());
|
|
129
|
+
// 解析行数变化
|
|
130
|
+
const insertMatch = line.match(/(\d+)\s+insertion/);
|
|
131
|
+
const deleteMatch = line.match(/(\d+)\s+deletion/);
|
|
132
|
+
if (insertMatch)
|
|
133
|
+
lineDelta += parseInt(insertMatch[1], 10);
|
|
134
|
+
if (deleteMatch)
|
|
135
|
+
lineDelta += parseInt(deleteMatch[1], 10);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
void error;
|
|
141
|
+
// git diff 失败(可能不在 git 仓库中)
|
|
142
|
+
}
|
|
143
|
+
// 如果没有 git diff 信息,尝试获取最近 commit 的变更
|
|
144
|
+
if (fileCount === 0) {
|
|
145
|
+
try {
|
|
146
|
+
const lastCommit = execSync('git diff --stat HEAD~1..HEAD --no-color 2>/dev/null', {
|
|
147
|
+
cwd: workingDir,
|
|
148
|
+
encoding: 'utf-8',
|
|
149
|
+
timeout: 5000,
|
|
150
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
151
|
+
}).trim();
|
|
152
|
+
if (lastCommit) {
|
|
153
|
+
const lines = lastCommit.split('\n').filter(l => l.trim() && !l.includes('files changed'));
|
|
154
|
+
fileCount = lines.length;
|
|
155
|
+
for (const line of lines) {
|
|
156
|
+
const fileMatch = line.match(/^\s*(.+?)\s*\|/);
|
|
157
|
+
if (fileMatch)
|
|
158
|
+
changedFiles.push(fileMatch[1].trim());
|
|
159
|
+
const insertMatch = line.match(/(\d+)\s+insertion/);
|
|
160
|
+
const deleteMatch = line.match(/(\d+)\s+deletion/);
|
|
161
|
+
if (insertMatch)
|
|
162
|
+
lineDelta += parseInt(insertMatch[1], 10);
|
|
163
|
+
if (deleteMatch)
|
|
164
|
+
lineDelta += parseInt(deleteMatch[1], 10);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
void error;
|
|
170
|
+
// 忽略
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return this.buildSignals(fileCount, lineDelta, changedFiles, '');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 从描述 + 文件列表采集信号
|
|
177
|
+
*/
|
|
178
|
+
collectDescriptionSignals(description, files) {
|
|
179
|
+
const lineDelta = 0;
|
|
180
|
+
return this.buildSignals(files.length, lineDelta, files, description);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 构建信号对象
|
|
184
|
+
*/
|
|
185
|
+
buildSignals(fileCount, lineDelta, changedFiles, description) {
|
|
186
|
+
// 跨模块检测:变更文件是否跨越 ≥2 个顶层目录
|
|
187
|
+
const topDirs = new Set();
|
|
188
|
+
for (const file of changedFiles) {
|
|
189
|
+
const parts = file.replace(/\\/g, '/').split('/');
|
|
190
|
+
if (parts.length > 1) {
|
|
191
|
+
topDirs.add(parts[0]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const crossModule = topDirs.size >= 2;
|
|
195
|
+
// 关键文件命中
|
|
196
|
+
const criticalFileHits = [];
|
|
197
|
+
for (const file of changedFiles) {
|
|
198
|
+
for (const pattern of CRITICAL_FILES) {
|
|
199
|
+
if (pattern.test(file)) {
|
|
200
|
+
criticalFileHits.push(file);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// 描述关键词提取
|
|
206
|
+
const descriptionKeywords = description.toLowerCase()
|
|
207
|
+
.split(/[\s,;.!?]+/)
|
|
208
|
+
.filter(w => w.length > 2);
|
|
209
|
+
return {
|
|
210
|
+
fileCount,
|
|
211
|
+
lineDelta,
|
|
212
|
+
crossModule,
|
|
213
|
+
criticalFileHits,
|
|
214
|
+
descriptionKeywords,
|
|
215
|
+
topDirs: Array.from(topDirs),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=TaskLevelDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskLevelDetector.js","sourceRoot":"","sources":["../../src/workflow/TaskLevelDetector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type TaskLevel } from './TaskLevelDetector.js';
|
|
2
|
+
export type PhaseName = 'define' | 'plan' | 'build' | 'verify' | 'review' | 'ship';
|
|
3
|
+
export interface RunOptions {
|
|
4
|
+
title: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
successCriteria?: string[];
|
|
7
|
+
level?: TaskLevel;
|
|
8
|
+
skipPhases?: PhaseName[];
|
|
9
|
+
stopOnFailure?: boolean;
|
|
10
|
+
autoCommit?: boolean;
|
|
11
|
+
scaleDir?: string;
|
|
12
|
+
projectDir?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PhaseResult {
|
|
15
|
+
phase: PhaseName;
|
|
16
|
+
success: boolean;
|
|
17
|
+
duration: number;
|
|
18
|
+
artifactId?: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
details?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export interface TokenUsage {
|
|
23
|
+
totalEstimated: number;
|
|
24
|
+
byPhase: Record<string, number>;
|
|
25
|
+
}
|
|
26
|
+
export interface RunResult {
|
|
27
|
+
success: boolean;
|
|
28
|
+
phases: PhaseResult[];
|
|
29
|
+
artifacts: {
|
|
30
|
+
needId?: string;
|
|
31
|
+
specId?: string;
|
|
32
|
+
planId?: string;
|
|
33
|
+
taskId?: string;
|
|
34
|
+
};
|
|
35
|
+
tokenUsage: TokenUsage;
|
|
36
|
+
duration: number;
|
|
37
|
+
}
|
|
38
|
+
export declare class WorkflowOrchestrator {
|
|
39
|
+
private eventBus;
|
|
40
|
+
private store;
|
|
41
|
+
private fsm;
|
|
42
|
+
private workflowEngine;
|
|
43
|
+
private scaleDir;
|
|
44
|
+
private projectDir;
|
|
45
|
+
constructor(opts?: {
|
|
46
|
+
scaleDir?: string;
|
|
47
|
+
projectDir?: string;
|
|
48
|
+
});
|
|
49
|
+
run(options: RunOptions): Promise<RunResult>;
|
|
50
|
+
private executeDefine;
|
|
51
|
+
private executePlan;
|
|
52
|
+
private executeBuild;
|
|
53
|
+
private executeVerify;
|
|
54
|
+
private executeReview;
|
|
55
|
+
private executeShip;
|
|
56
|
+
private buildResult;
|
|
57
|
+
/** Close database connections */
|
|
58
|
+
close(): void;
|
|
59
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// SCALE Engine — Workflow Orchestrator
|
|
2
|
+
// Chains define → plan → build → verify → review → ship automatically
|
|
3
|
+
import { EventBus } from '../core/eventBus.js';
|
|
4
|
+
import { SQLiteArtifactStore } from '../artifact/sqliteStore.js';
|
|
5
|
+
import { FSM } from '../artifact/fsm.js';
|
|
6
|
+
import { registerAllFSMs } from '../artifact/fsmDefinitions.js';
|
|
7
|
+
import { CapabilityRegistry } from '../capabilities/CapabilityRegistry.js';
|
|
8
|
+
import { SkillRegistry } from '../skills/SkillRegistry.js';
|
|
9
|
+
import { registerCoreSkills } from '../skills/coreSkills.js';
|
|
10
|
+
import { registerExternalSkills } from '../skills/ExternalSkills.js';
|
|
11
|
+
import { WorkflowEngine } from './WorkflowEngine.js';
|
|
12
|
+
import { TaskLevelDetector } from './TaskLevelDetector.js';
|
|
13
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { estimateTokens } from '../context/ContextBudget.js';
|
|
16
|
+
export class WorkflowOrchestrator {
|
|
17
|
+
constructor(opts = {}) {
|
|
18
|
+
this.scaleDir = opts.scaleDir ?? '.scale';
|
|
19
|
+
this.projectDir = opts.projectDir ?? process.cwd();
|
|
20
|
+
if (!existsSync(this.scaleDir))
|
|
21
|
+
mkdirSync(this.scaleDir, { recursive: true });
|
|
22
|
+
this.eventBus = new EventBus({ eventsDir: join(this.scaleDir, 'events') });
|
|
23
|
+
this.store = new SQLiteArtifactStore(this.eventBus, {
|
|
24
|
+
dbPath: join(this.scaleDir, 'scale.db'),
|
|
25
|
+
artifactsDir: join(this.scaleDir, 'artifacts'),
|
|
26
|
+
});
|
|
27
|
+
this.fsm = new FSM(this.store, this.eventBus);
|
|
28
|
+
registerAllFSMs(this.fsm);
|
|
29
|
+
const capabilityRegistry = new CapabilityRegistry(this.eventBus);
|
|
30
|
+
const skillRegistry = new SkillRegistry(this.eventBus);
|
|
31
|
+
registerCoreSkills(skillRegistry);
|
|
32
|
+
registerExternalSkills(skillRegistry, this.eventBus);
|
|
33
|
+
this.workflowEngine = new WorkflowEngine({
|
|
34
|
+
eventBus: this.eventBus,
|
|
35
|
+
capabilityRegistry,
|
|
36
|
+
skillRegistry,
|
|
37
|
+
scaleDir: this.scaleDir,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async run(options) {
|
|
41
|
+
const startTime = Date.now();
|
|
42
|
+
const phases = [];
|
|
43
|
+
const artifacts = {};
|
|
44
|
+
const tokenByPhase = {};
|
|
45
|
+
const skipSet = new Set(options.skipPhases ?? []);
|
|
46
|
+
const stopOnFailure = options.stopOnFailure !== false;
|
|
47
|
+
const execute = async (name, fn) => {
|
|
48
|
+
if (skipSet.has(name)) {
|
|
49
|
+
phases.push({ phase: name, success: true, duration: 0 });
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
const phaseStart = Date.now();
|
|
53
|
+
try {
|
|
54
|
+
const result = await fn();
|
|
55
|
+
const detailStr = JSON.stringify(result.details ?? {});
|
|
56
|
+
tokenByPhase[name] = (tokenByPhase[name] ?? 0) + estimateTokens(detailStr);
|
|
57
|
+
phases.push({ phase: name, success: true, duration: Date.now() - phaseStart, ...result });
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
62
|
+
phases.push({ phase: name, success: false, duration: Date.now() - phaseStart, error: message });
|
|
63
|
+
if (stopOnFailure)
|
|
64
|
+
return false;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
// Phase 1: DEFINE
|
|
69
|
+
const defineOk = await execute('define', async () => {
|
|
70
|
+
const result = await this.executeDefine(options);
|
|
71
|
+
artifacts.needId = result.needId;
|
|
72
|
+
artifacts.specId = result.specId;
|
|
73
|
+
return { artifactId: result.specId, details: result };
|
|
74
|
+
});
|
|
75
|
+
if (!defineOk)
|
|
76
|
+
return this.buildResult(false, phases, artifacts, tokenByPhase, startTime);
|
|
77
|
+
// Phase 2: PLAN
|
|
78
|
+
const planOk = await execute('plan', async () => {
|
|
79
|
+
const result = await this.executePlan(artifacts.specId, options);
|
|
80
|
+
artifacts.planId = result.planId;
|
|
81
|
+
return { artifactId: result.planId, details: result };
|
|
82
|
+
});
|
|
83
|
+
if (!planOk)
|
|
84
|
+
return this.buildResult(false, phases, artifacts, tokenByPhase, startTime);
|
|
85
|
+
// Phase 3: BUILD
|
|
86
|
+
const buildOk = await execute('build', async () => {
|
|
87
|
+
const result = await this.executeBuild(artifacts.planId, options);
|
|
88
|
+
artifacts.taskId = result.taskId;
|
|
89
|
+
return { artifactId: result.taskId, details: result };
|
|
90
|
+
});
|
|
91
|
+
if (!buildOk)
|
|
92
|
+
return this.buildResult(false, phases, artifacts, tokenByPhase, startTime);
|
|
93
|
+
// Phase 4: VERIFY
|
|
94
|
+
const verifyOk = await execute('verify', async () => {
|
|
95
|
+
const result = await this.executeVerify(artifacts.taskId);
|
|
96
|
+
return { details: result };
|
|
97
|
+
});
|
|
98
|
+
if (!verifyOk)
|
|
99
|
+
return this.buildResult(false, phases, artifacts, tokenByPhase, startTime);
|
|
100
|
+
// Phase 5: REVIEW
|
|
101
|
+
const reviewOk = await execute('review', async () => {
|
|
102
|
+
const result = await this.executeReview(artifacts.taskId);
|
|
103
|
+
return { details: result };
|
|
104
|
+
});
|
|
105
|
+
if (!reviewOk)
|
|
106
|
+
return this.buildResult(false, phases, artifacts, tokenByPhase, startTime);
|
|
107
|
+
// Phase 6: SHIP
|
|
108
|
+
await execute('ship', async () => {
|
|
109
|
+
const result = await this.executeShip(artifacts.taskId, options);
|
|
110
|
+
return { details: result };
|
|
111
|
+
});
|
|
112
|
+
return this.buildResult(phases.every(p => p.success), phases, artifacts, tokenByPhase, startTime);
|
|
113
|
+
}
|
|
114
|
+
async executeDefine(options) {
|
|
115
|
+
const desc = options.description ?? options.title;
|
|
116
|
+
const successCriteria = options.successCriteria ?? ['Feature works as described', 'No regression'];
|
|
117
|
+
// Run ambiguity analysis (warning only, don't block orchestrator)
|
|
118
|
+
const ambiguityResult = this.workflowEngine.getAmbiguityScorer().analyzeRequirement(desc);
|
|
119
|
+
// Create Need
|
|
120
|
+
const need = await this.store.create({
|
|
121
|
+
type: 'Need',
|
|
122
|
+
title: options.title,
|
|
123
|
+
payload: { rawText: desc },
|
|
124
|
+
initialStatus: 'DRAFT',
|
|
125
|
+
createdBy: { kind: 'human', userId: 'orchestrator' },
|
|
126
|
+
});
|
|
127
|
+
// Create Spec
|
|
128
|
+
const specPayload = {
|
|
129
|
+
what: desc,
|
|
130
|
+
successCriteria,
|
|
131
|
+
outOfScope: [],
|
|
132
|
+
edgeCases: [],
|
|
133
|
+
northStar: 'Deliver user value',
|
|
134
|
+
ambiguityScore: ambiguityResult.totalScore,
|
|
135
|
+
};
|
|
136
|
+
const spec = await this.store.create({
|
|
137
|
+
type: 'Spec',
|
|
138
|
+
title: options.title,
|
|
139
|
+
payload: specPayload,
|
|
140
|
+
parents: [need.id],
|
|
141
|
+
initialStatus: 'DRAFT',
|
|
142
|
+
createdBy: { kind: 'human', userId: 'orchestrator' },
|
|
143
|
+
});
|
|
144
|
+
// FSM transitions (approve may fail on high ambiguity - that's OK for orchestrator)
|
|
145
|
+
await this.fsm.transition(spec.id, 'refine', { actor: { kind: 'system', component: 'orchestrator' } });
|
|
146
|
+
const approveResult = await this.fsm.transition(spec.id, 'approve', { actor: { kind: 'system', component: 'orchestrator' } });
|
|
147
|
+
if (!approveResult.success) {
|
|
148
|
+
// Spec stays in REVIEWING — not fatal for orchestrator flow
|
|
149
|
+
}
|
|
150
|
+
return { needId: need.id, specId: spec.id, ambiguityScore: ambiguityResult.totalScore };
|
|
151
|
+
}
|
|
152
|
+
async executePlan(specId, options) {
|
|
153
|
+
const spec = await this.store.get(specId);
|
|
154
|
+
if (!spec || spec.type !== 'Spec')
|
|
155
|
+
throw new Error(`Spec not found: ${specId}`);
|
|
156
|
+
const specDesc = spec.payload.what;
|
|
157
|
+
const consensusResult = await this.workflowEngine.plan(specDesc, {
|
|
158
|
+
persistArtifact: false,
|
|
159
|
+
runGate: false,
|
|
160
|
+
});
|
|
161
|
+
const rollbackStrategy = consensusResult.preMortem.mitigations.join('\n') || 'Revert git commits';
|
|
162
|
+
const planPayload = {
|
|
163
|
+
approach: consensusResult.viableOptions.find(o => o.selected)?.description ?? 'Standard implementation',
|
|
164
|
+
techChoices: [],
|
|
165
|
+
modules: [],
|
|
166
|
+
rollbackStrategy,
|
|
167
|
+
estimatedComplexity: 5,
|
|
168
|
+
};
|
|
169
|
+
const plan = await this.store.create({
|
|
170
|
+
type: 'Plan',
|
|
171
|
+
title: `Plan for ${spec.title}`,
|
|
172
|
+
payload: planPayload,
|
|
173
|
+
parents: [specId],
|
|
174
|
+
initialStatus: 'DRAFT',
|
|
175
|
+
createdBy: { kind: 'human', userId: 'orchestrator' },
|
|
176
|
+
});
|
|
177
|
+
await this.fsm.transition(plan.id, 'review', { actor: { kind: 'system', component: 'orchestrator' } });
|
|
178
|
+
return { planId: plan.id };
|
|
179
|
+
}
|
|
180
|
+
async executeBuild(planId, options) {
|
|
181
|
+
const plan = await this.store.get(planId);
|
|
182
|
+
if (!plan || plan.type !== 'Plan')
|
|
183
|
+
throw new Error(`Plan not found: ${planId}`);
|
|
184
|
+
// Auto-detect level if not specified
|
|
185
|
+
let level;
|
|
186
|
+
if (options.level) {
|
|
187
|
+
level = options.level;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
const detector = new TaskLevelDetector();
|
|
191
|
+
const detection = await detector.detectFromGitDiff(this.projectDir);
|
|
192
|
+
level = detection.level;
|
|
193
|
+
}
|
|
194
|
+
const taskPayload = {
|
|
195
|
+
description: options.description ?? `Implement ${plan.title}`,
|
|
196
|
+
workflowLevel: level,
|
|
197
|
+
servicesTouched: [],
|
|
198
|
+
filesInvolved: [],
|
|
199
|
+
dependsOn: [],
|
|
200
|
+
requiredRole: 'implementer',
|
|
201
|
+
requiredCapabilities: ['code-generation', 'file-editing'],
|
|
202
|
+
buildStatus: 'pending',
|
|
203
|
+
lintStatus: 'pending',
|
|
204
|
+
agentBrief: {
|
|
205
|
+
category: 'enhancement',
|
|
206
|
+
summary: options.description ?? `Implement ${plan.title}`,
|
|
207
|
+
currentBehavior: 'Feature not yet implemented',
|
|
208
|
+
desiredBehavior: `Implement: ${plan.title}`,
|
|
209
|
+
keyInterfaces: [],
|
|
210
|
+
acceptanceCriteria: [],
|
|
211
|
+
outOfScope: [],
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
const task = await this.store.create({
|
|
215
|
+
type: 'Task',
|
|
216
|
+
title: `Task for ${plan.title}`,
|
|
217
|
+
payload: taskPayload,
|
|
218
|
+
parents: [planId],
|
|
219
|
+
initialStatus: 'PENDING',
|
|
220
|
+
createdBy: { kind: 'human', userId: 'orchestrator' },
|
|
221
|
+
});
|
|
222
|
+
// FSM transitions
|
|
223
|
+
await this.fsm.transition(task.id, 'schedule', { actor: { kind: 'system', component: 'orchestrator' } });
|
|
224
|
+
await this.fsm.transition(task.id, 'start', { actor: { kind: 'human', userId: 'orchestrator' } });
|
|
225
|
+
// Update Plan status
|
|
226
|
+
const canImplement = await this.fsm.canTransition(planId, 'implement');
|
|
227
|
+
if (canImplement.allowed) {
|
|
228
|
+
await this.fsm.transition(planId, 'implement', { actor: { kind: 'system', component: 'orchestrator' } });
|
|
229
|
+
}
|
|
230
|
+
return { taskId: task.id, level };
|
|
231
|
+
}
|
|
232
|
+
async executeVerify(taskId) {
|
|
233
|
+
const task = await this.store.get(taskId);
|
|
234
|
+
if (!task || task.type !== 'Task')
|
|
235
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
236
|
+
// Run verification gates
|
|
237
|
+
const gateResults = await this.workflowEngine.verify({
|
|
238
|
+
cwd: this.projectDir,
|
|
239
|
+
});
|
|
240
|
+
const passed = gateResults.every(r => r.passed);
|
|
241
|
+
// Update task payload
|
|
242
|
+
const payload = task.payload;
|
|
243
|
+
const buildPassed = gateResults.filter(r => r.gate === 'G0').every(r => r.passed);
|
|
244
|
+
const lintPassed = gateResults.filter(r => r.gate === 'G4').every(r => r.passed);
|
|
245
|
+
const testPassed = gateResults.filter(r => r.gate === 'G5').every(r => r.passed);
|
|
246
|
+
const updatedPayload = {
|
|
247
|
+
...payload,
|
|
248
|
+
buildStatus: buildPassed ? 'success' : 'failed',
|
|
249
|
+
lintStatus: lintPassed ? 'success' : 'failed',
|
|
250
|
+
testPassed,
|
|
251
|
+
verifiedAt: Date.now(),
|
|
252
|
+
};
|
|
253
|
+
await this.store.update(taskId, { payload: updatedPayload });
|
|
254
|
+
return { passed, gateCount: gateResults.length };
|
|
255
|
+
}
|
|
256
|
+
async executeReview(taskId) {
|
|
257
|
+
const task = await this.store.get(taskId);
|
|
258
|
+
if (!task || task.type !== 'Task')
|
|
259
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
260
|
+
// Run Karpathy check
|
|
261
|
+
const karpathyResult = this.workflowEngine.checkKarpathy({
|
|
262
|
+
hypothesesListed: true,
|
|
263
|
+
hasExtraFeatures: false,
|
|
264
|
+
changesTraceable: true,
|
|
265
|
+
hasVerifiableGoal: true,
|
|
266
|
+
});
|
|
267
|
+
const passed = karpathyResult.every(check => check.passed);
|
|
268
|
+
// Update task payload
|
|
269
|
+
const payload = task.payload;
|
|
270
|
+
const updatedPayload = {
|
|
271
|
+
...payload,
|
|
272
|
+
reviewPassed: passed,
|
|
273
|
+
reviewedAt: Date.now(),
|
|
274
|
+
};
|
|
275
|
+
await this.store.update(taskId, { payload: updatedPayload });
|
|
276
|
+
return { passed, criticalCount: passed ? 0 : 1 };
|
|
277
|
+
}
|
|
278
|
+
async executeShip(taskId, options) {
|
|
279
|
+
const task = await this.store.get(taskId);
|
|
280
|
+
if (!task || task.type !== 'Task')
|
|
281
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
282
|
+
const payload = task.payload;
|
|
283
|
+
// Verify prerequisites
|
|
284
|
+
if (payload.buildStatus !== 'success')
|
|
285
|
+
throw new Error('Build not passed');
|
|
286
|
+
if (!payload.testPassed)
|
|
287
|
+
throw new Error('Tests not passed');
|
|
288
|
+
if (!payload.reviewPassed)
|
|
289
|
+
throw new Error('Review not passed');
|
|
290
|
+
// Complete FSM transition
|
|
291
|
+
if (task.status !== 'COMPLETED') {
|
|
292
|
+
const canComplete = await this.fsm.canTransition(taskId, 'complete');
|
|
293
|
+
if (canComplete.allowed) {
|
|
294
|
+
await this.fsm.transition(taskId, 'complete', { actor: { kind: 'human', userId: 'orchestrator' } });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Complete parent plan
|
|
298
|
+
if (task.parents.length > 0) {
|
|
299
|
+
try {
|
|
300
|
+
await this.fsm.transition(task.parents[0], 'complete', {
|
|
301
|
+
actor: { kind: 'system', component: 'orchestrator' },
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
const parentPlanWarning = error instanceof Error ? error.message : String(error);
|
|
306
|
+
void parentPlanWarning;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return { committed: options.autoCommit !== false };
|
|
310
|
+
}
|
|
311
|
+
buildResult(success, phases, artifacts, tokenByPhase, startTime) {
|
|
312
|
+
const totalEstimated = Object.values(tokenByPhase).reduce((s, v) => s + v, 0);
|
|
313
|
+
return {
|
|
314
|
+
success,
|
|
315
|
+
phases,
|
|
316
|
+
artifacts,
|
|
317
|
+
tokenUsage: { totalEstimated, byPhase: { ...tokenByPhase } },
|
|
318
|
+
duration: Date.now() - startTime,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/** Close database connections */
|
|
322
|
+
close() {
|
|
323
|
+
this.store.close?.();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=WorkflowOrchestrator.js.map
|