@hongmaple0820/scale-engine 0.18.0 → 0.19.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 +296 -237
- package/README.md +157 -63
- package/dist/api/cli.js +448 -27
- package/dist/api/cli.js.map +1 -1
- package/dist/api/doctor.d.ts +4 -1
- package/dist/api/doctor.js +85 -1
- package/dist/api/doctor.js.map +1 -1
- package/dist/api/quickstart.d.ts +3 -0
- package/dist/api/quickstart.js +9 -4
- package/dist/api/quickstart.js.map +1 -1
- package/dist/cli/phaseCommands.js +7 -0
- package/dist/cli/phaseCommands.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/MemoryFabric.d.ts +118 -0
- package/dist/memory/MemoryFabric.js +281 -0
- package/dist/memory/MemoryFabric.js.map +1 -0
- package/dist/memory/MemoryLearning.d.ts +61 -0
- package/dist/memory/MemoryLearning.js +203 -0
- package/dist/memory/MemoryLearning.js.map +1 -0
- package/dist/memory/index.d.ts +2 -0
- package/dist/memory/index.js +3 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/output/HTMLArtifactLayer.js +31 -31
- package/dist/prompts/VibeTemplateGallery.js +121 -121
- package/dist/runtime/FinalReportGuard.d.ts +16 -0
- package/dist/runtime/FinalReportGuard.js +14 -0
- package/dist/runtime/FinalReportGuard.js.map +1 -0
- package/dist/runtime/RuntimeDoctor.d.ts +23 -0
- package/dist/runtime/RuntimeDoctor.js +151 -0
- package/dist/runtime/RuntimeDoctor.js.map +1 -0
- package/dist/runtime/RuntimeEvidenceLedger.d.ts +50 -0
- package/dist/runtime/RuntimeEvidenceLedger.js +89 -0
- package/dist/runtime/RuntimeEvidenceLedger.js.map +1 -0
- package/dist/runtime/SessionLedger.d.ts +53 -0
- package/dist/runtime/SessionLedger.js +104 -0
- package/dist/runtime/SessionLedger.js.map +1 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/workflow/EngineeringStandards.js +69 -66
- package/dist/workflow/EngineeringStandards.js.map +1 -1
- package/dist/workflow/GovernanceTemplatePacks.js +126 -126
- package/dist/workflow/GovernanceTemplates.d.ts +1 -1
- package/dist/workflow/GovernanceTemplates.js +489 -218
- package/dist/workflow/GovernanceTemplates.js.map +1 -1
- package/dist/workflow/ResourceGovernance.js +27 -18
- package/dist/workflow/ResourceGovernance.js.map +1 -1
- package/dist/workflow/VerificationCommands.d.ts +11 -0
- package/dist/workflow/VerificationCommands.js +2 -0
- package/dist/workflow/VerificationCommands.js.map +1 -1
- package/dist/workflow/VerificationProfile.d.ts +2 -1
- package/dist/workflow/VerificationProfile.js +3 -0
- package/dist/workflow/VerificationProfile.js.map +1 -1
- package/dist/workflow/WorkflowArtifactWriter.js +2 -1
- package/dist/workflow/WorkflowArtifactWriter.js.map +1 -1
- package/dist/workflow/WorkflowEngine.js +4 -1
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/dist/workflow/WorkspaceSafety.d.ts +9 -0
- package/dist/workflow/WorkspaceSafety.js +49 -0
- package/dist/workflow/WorkspaceSafety.js.map +1 -0
- package/dist/workflow/gates/GateSystem.d.ts +12 -1
- package/dist/workflow/gates/GateSystem.js +106 -0
- package/dist/workflow/gates/GateSystem.js.map +1 -1
- package/dist/workflow/types.d.ts +1 -1
- package/docs/MEMORY_FABRIC.md +107 -0
- package/docs/README.md +68 -0
- package/docs/RUNTIME_EVIDENCE.md +101 -0
- package/docs/start/README.md +42 -0
- package/docs/start/agent-governance-demo.md +107 -0
- package/docs/start/quickstart.md +127 -0
- package/examples/demo-projects/agent-governance-demo/README.md +37 -0
- package/examples/demo-projects/agent-governance-demo/package.json +16 -0
- package/examples/demo-projects/agent-governance-demo/src/oauth-state.ts +39 -0
- package/examples/demo-projects/agent-governance-demo/tests/oauth-state.test.ts +52 -0
- package/package.json +8 -3
package/dist/api/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ import { createSkillPlan, evaluateSkillGate, loadSkillRoutingPolicy, skillPlanMa
|
|
|
20
20
|
import { createAdapter, SUPPORTED_AGENTS } from '../adapters/index.js';
|
|
21
21
|
import { LessonExtractor, RuleProposer, HookGenerator, EvolutionEngine } from '../evolution/EvolutionEngine.js';
|
|
22
22
|
import { Doctor } from './doctor.js';
|
|
23
|
-
import { quickStart, detectPlatform } from './quickstart.js';
|
|
23
|
+
import { quickStart, detectPlatform, governanceNextSteps } from './quickstart.js';
|
|
24
24
|
import { SkillDiscovery } from '../skills/SkillDiscovery.js';
|
|
25
25
|
import { inspectRequiredWorkflowSkills, inspectWorkflowSkills } from '../skills/SkillDoctor.js';
|
|
26
26
|
import { evaluateSkillInstallSafety, listSkillRepositoryEntries, recommendSkillWorkflow, renderSkillRepositoryMarkdown, } from '../skills/SkillRepository.js';
|
|
@@ -49,9 +49,12 @@ import { ToolOrchestrator } from '../tools/ToolOrchestrator.js';
|
|
|
49
49
|
import { loadToolPolicy, toolPolicyTemplate } from '../tools/ToolPolicy.js';
|
|
50
50
|
import { doctorHtmlArtifacts, renderHtmlArtifact, resolveHtmlArtifactForOpen, settleHtmlArtifacts, } from '../output/HTMLArtifactLayer.js';
|
|
51
51
|
import { cleanupWorkspaceLifecycle, inspectWorkspaceLifecycle, } from '../workflow/WorkspaceLifecycle.js';
|
|
52
|
+
import { inspectWorkspaceSafety } from '../workflow/WorkspaceSafety.js';
|
|
53
|
+
import { RuntimeEvidenceLedger, SessionLedger, doctorRuntimeEvidence, evaluateFinalReportReadiness, } from '../runtime/index.js';
|
|
54
|
+
import { MemoryFabric, doctorMemoryFabric, renderContextPackMarkdown, renderMemoryLearningCandidateMarkdown, settleMemoryLearning, } from '../memory/index.js';
|
|
52
55
|
import { resolveWorkspaceTopology, workspaceTopologyPath, workspaceTopologyTemplate, } from '../workflow/WorkspaceTopology.js';
|
|
53
56
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
54
|
-
import { join, resolve } from 'node:path';
|
|
57
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
55
58
|
import { execFileSync } from 'node:child_process';
|
|
56
59
|
import { pathToFileURL } from 'node:url';
|
|
57
60
|
import { SCALE_ENGINE_VERSION } from '../version.js';
|
|
@@ -131,15 +134,15 @@ function evaluateEngineeringStandardsGate(options) {
|
|
|
131
134
|
}
|
|
132
135
|
const settlement = options.settle && options.artifactsDir
|
|
133
136
|
? settleEngineeringStandards({
|
|
134
|
-
projectDir: PROJECT_DIR,
|
|
135
|
-
scaleDir: SCALE_DIR,
|
|
137
|
+
projectDir: options.projectDir ?? PROJECT_DIR,
|
|
138
|
+
scaleDir: options.scaleDir ?? SCALE_DIR,
|
|
136
139
|
taskId: options.taskId,
|
|
137
140
|
artifactsDir: options.artifactsDir,
|
|
138
141
|
})
|
|
139
142
|
: undefined;
|
|
140
143
|
const doctor = settlement?.doctor ?? doctorEngineeringStandards({
|
|
141
|
-
projectDir: PROJECT_DIR,
|
|
142
|
-
scaleDir: SCALE_DIR,
|
|
144
|
+
projectDir: options.projectDir ?? PROJECT_DIR,
|
|
145
|
+
scaleDir: options.scaleDir ?? SCALE_DIR,
|
|
143
146
|
});
|
|
144
147
|
return {
|
|
145
148
|
mode,
|
|
@@ -151,6 +154,16 @@ function evaluateEngineeringStandardsGate(options) {
|
|
|
151
154
|
standardsImpactPath: settlement?.standardsImpactPath,
|
|
152
155
|
};
|
|
153
156
|
}
|
|
157
|
+
function skippedEngineeringStandardsGate(reason, policy) {
|
|
158
|
+
void reason;
|
|
159
|
+
return {
|
|
160
|
+
mode: normalizeEngineeringStandardsGateMode(policy.engineeringStandardsGate),
|
|
161
|
+
checked: false,
|
|
162
|
+
blocked: false,
|
|
163
|
+
ok: true,
|
|
164
|
+
findings: [],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
154
167
|
function normalizeEngineeringStandardsGateMode(value) {
|
|
155
168
|
return value === 'off' || value === 'block' ? value : 'warn';
|
|
156
169
|
}
|
|
@@ -194,6 +207,23 @@ function createEngine() {
|
|
|
194
207
|
});
|
|
195
208
|
return { eventBus, store, fsm, gateway, roleGate, kb, ctx, fsmAgentBridge, workflowEngine };
|
|
196
209
|
}
|
|
210
|
+
function resolveScaleDirForProject(projectDir) {
|
|
211
|
+
return isAbsolute(SCALE_DIR) ? SCALE_DIR : join(projectDir, SCALE_DIR);
|
|
212
|
+
}
|
|
213
|
+
function createVerificationWorkflowEngine(scaleDir) {
|
|
214
|
+
ensureDir(scaleDir);
|
|
215
|
+
const eventBus = new EventBus({ eventsDir: join(scaleDir, 'events') });
|
|
216
|
+
const capabilityRegistry = new CapabilityRegistry(eventBus);
|
|
217
|
+
const skillRegistry = new SkillRegistry(eventBus);
|
|
218
|
+
registerCoreSkills(skillRegistry);
|
|
219
|
+
registerExternalSkills(skillRegistry, eventBus);
|
|
220
|
+
return new WorkflowEngine({
|
|
221
|
+
eventBus,
|
|
222
|
+
capabilityRegistry,
|
|
223
|
+
skillRegistry,
|
|
224
|
+
scaleDir,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
197
227
|
// ============================================================================
|
|
198
228
|
// session commands
|
|
199
229
|
// ============================================================================
|
|
@@ -1379,6 +1409,7 @@ function normalizeWorkspaceTopologyKind(value) {
|
|
|
1379
1409
|
const preflight = defineCommand({
|
|
1380
1410
|
meta: { name: 'preflight', description: 'Run service-aware verification without a task artifact' },
|
|
1381
1411
|
args: {
|
|
1412
|
+
dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
|
|
1382
1413
|
'build-cmd': { type: 'string', description: 'Override build command' },
|
|
1383
1414
|
'lint-cmd': { type: 'string', description: 'Override lint command' },
|
|
1384
1415
|
'test-cmd': { type: 'string', description: 'Override test command' },
|
|
@@ -1391,22 +1422,32 @@ const preflight = defineCommand({
|
|
|
1391
1422
|
json: { type: 'boolean', default: false },
|
|
1392
1423
|
},
|
|
1393
1424
|
async run({ args }) {
|
|
1394
|
-
const
|
|
1425
|
+
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
1426
|
+
const scaleDir = resolveScaleDirForProject(projectDir);
|
|
1427
|
+
const workflowEngine = createVerificationWorkflowEngine(scaleDir);
|
|
1395
1428
|
const preflightProfile = normalizePreflightProfile(args['preflight-profile']);
|
|
1396
|
-
const gateStages = gatesForPreflightProfile(preflightProfile);
|
|
1397
1429
|
const resolved = resolveVerificationTargets({
|
|
1398
|
-
projectDir
|
|
1399
|
-
scaleDir
|
|
1430
|
+
projectDir,
|
|
1431
|
+
scaleDir,
|
|
1400
1432
|
profile: args.profile,
|
|
1401
1433
|
service: args.service,
|
|
1402
1434
|
});
|
|
1435
|
+
let gateStages = gatesForPreflightProfile(preflightProfile);
|
|
1436
|
+
if (resolved.targets.some(target => target.config.smoke)) {
|
|
1437
|
+
gateStages = ['G8'];
|
|
1438
|
+
}
|
|
1403
1439
|
const commandTargetsSkipped = shouldSkipPreflightCommandTargets(resolved, args);
|
|
1404
1440
|
if (commandTargetsSkipped) {
|
|
1405
1441
|
resolved.warnings.push('No verification services or profile commands configured; command gates skipped for this governance-only project.');
|
|
1406
1442
|
}
|
|
1407
|
-
const
|
|
1408
|
-
|
|
1409
|
-
|
|
1443
|
+
const workspaceSafety = inspectWorkspaceSafety(projectDir);
|
|
1444
|
+
const engineeringStandards = workspaceSafety.blocked
|
|
1445
|
+
? skippedEngineeringStandardsGate('Workspace has unresolved git conflicts; resolve them before standards scanning.', resolved.policy)
|
|
1446
|
+
: evaluateEngineeringStandardsGate({
|
|
1447
|
+
policy: resolved.policy,
|
|
1448
|
+
projectDir,
|
|
1449
|
+
scaleDir,
|
|
1450
|
+
});
|
|
1410
1451
|
const targetResults = [];
|
|
1411
1452
|
if (!args.json) {
|
|
1412
1453
|
console.log('\nSCALE Preflight');
|
|
@@ -1415,6 +1456,9 @@ const preflight = defineCommand({
|
|
|
1415
1456
|
console.log(` Profile: ${resolved.profileName}`);
|
|
1416
1457
|
console.log(` Preflight profile: ${preflightProfile}`);
|
|
1417
1458
|
console.log(` Gates: ${gateStages.join(', ')}`);
|
|
1459
|
+
if (workspaceSafety.blocked) {
|
|
1460
|
+
console.log(` Workspace safety: BLOCKED - ${workspaceSafety.message}`);
|
|
1461
|
+
}
|
|
1418
1462
|
if (engineeringStandards.checked) {
|
|
1419
1463
|
const status = engineeringStandards.blocked ? 'BLOCKED' : engineeringStandards.ok ? 'OK' : 'WARN';
|
|
1420
1464
|
console.log(` Engineering standards: ${status} (${engineeringStandards.mode})`);
|
|
@@ -1423,7 +1467,7 @@ const preflight = defineCommand({
|
|
|
1423
1467
|
console.log(' Engineering standards: skipped');
|
|
1424
1468
|
}
|
|
1425
1469
|
}
|
|
1426
|
-
for (const target of commandTargetsSkipped ? [] : resolved.targets) {
|
|
1470
|
+
for (const target of commandTargetsSkipped || workspaceSafety.blocked ? [] : resolved.targets) {
|
|
1427
1471
|
if (!args.json) {
|
|
1428
1472
|
const label = target.service ? `${target.service.name} (${target.service.path})` : 'root';
|
|
1429
1473
|
console.log(`\n Target: ${label}`);
|
|
@@ -1434,6 +1478,12 @@ const preflight = defineCommand({
|
|
|
1434
1478
|
lint: args['lint-cmd'] ?? target.config.lint,
|
|
1435
1479
|
test: args['test-cmd'] ?? target.config.test,
|
|
1436
1480
|
coverage: args['coverage-cmd'] ?? target.config.coverage,
|
|
1481
|
+
smoke: target.config.smoke,
|
|
1482
|
+
runtimeEvidence: {
|
|
1483
|
+
projectDir,
|
|
1484
|
+
scaleDir,
|
|
1485
|
+
profile: resolved.profileName,
|
|
1486
|
+
},
|
|
1437
1487
|
tddEvidence: args['tdd-evidence'],
|
|
1438
1488
|
tddStrict: isTruthyFlag(args['tdd-strict']),
|
|
1439
1489
|
gates: gateStages,
|
|
@@ -1441,7 +1491,7 @@ const preflight = defineCommand({
|
|
|
1441
1491
|
const passed = gates.every(gate => gate.passed);
|
|
1442
1492
|
targetResults.push({
|
|
1443
1493
|
service: target.service?.name,
|
|
1444
|
-
cwd: target.config.cwd ??
|
|
1494
|
+
cwd: target.config.cwd ?? projectDir,
|
|
1445
1495
|
gates,
|
|
1446
1496
|
passed,
|
|
1447
1497
|
});
|
|
@@ -1454,6 +1504,7 @@ const preflight = defineCommand({
|
|
|
1454
1504
|
}
|
|
1455
1505
|
}
|
|
1456
1506
|
const passed = (targetResults.length === 0 || targetResults.every(target => target.passed)) &&
|
|
1507
|
+
!workspaceSafety.blocked &&
|
|
1457
1508
|
!engineeringStandards.blocked;
|
|
1458
1509
|
const result = {
|
|
1459
1510
|
phase: 'PREFLIGHT',
|
|
@@ -1462,6 +1513,7 @@ const preflight = defineCommand({
|
|
|
1462
1513
|
gates: gateStages,
|
|
1463
1514
|
services: targetResults.map(target => target.service).filter(Boolean),
|
|
1464
1515
|
policy: resolved.policy,
|
|
1516
|
+
workspaceSafety,
|
|
1465
1517
|
engineeringStandards,
|
|
1466
1518
|
targets: targetResults,
|
|
1467
1519
|
commandTargetsSkipped,
|
|
@@ -1497,7 +1549,7 @@ const status = defineCommand({
|
|
|
1497
1549
|
const latestTask = tasks[0];
|
|
1498
1550
|
const taskPayload = latestTask?.payload;
|
|
1499
1551
|
const workflowState = new WorkflowArtifactWriter(SCALE_DIR).readCurrentState();
|
|
1500
|
-
const currentOpenTasks = workflowState?.
|
|
1552
|
+
const currentOpenTasks = workflowState?.openTasks ?? [];
|
|
1501
1553
|
const nextOpenTask = nextWorkflowOpenTask(currentOpenTasks);
|
|
1502
1554
|
const blockers = [];
|
|
1503
1555
|
const latestBlockingEvidence = latestEvidence.find(record => !record.passed);
|
|
@@ -1513,16 +1565,16 @@ const status = defineCommand({
|
|
|
1513
1565
|
blockers.push(`Task ${latestTask.id} has no persisted review evidence`);
|
|
1514
1566
|
}
|
|
1515
1567
|
const nextCommand = (() => {
|
|
1568
|
+
if (nextOpenTask?.kind === 'command')
|
|
1569
|
+
return nextOpenTask.value;
|
|
1570
|
+
if (nextOpenTask?.kind === 'blocker')
|
|
1571
|
+
return `Resolve workflow blocker: ${nextOpenTask.value}`;
|
|
1516
1572
|
if (!specs[0])
|
|
1517
1573
|
return 'scale define "<feature>" --description "<what to build>"';
|
|
1518
1574
|
if (!plans[0])
|
|
1519
1575
|
return `scale plan ${specs[0].id}`;
|
|
1520
1576
|
if (!latestTask)
|
|
1521
1577
|
return `scale build ${plans[0].id}`;
|
|
1522
|
-
if (nextOpenTask?.kind === 'command')
|
|
1523
|
-
return nextOpenTask.value;
|
|
1524
|
-
if (nextOpenTask?.kind === 'blocker')
|
|
1525
|
-
return `Resolve workflow blocker: ${nextOpenTask.value}`;
|
|
1526
1578
|
if (!taskPayload?.verificationEvidenceIds?.length)
|
|
1527
1579
|
return `scale verify ${latestTask.id}`;
|
|
1528
1580
|
if (latestTask.status !== 'COMPLETED')
|
|
@@ -1569,7 +1621,7 @@ const status = defineCommand({
|
|
|
1569
1621
|
level: workflowState.level,
|
|
1570
1622
|
phase: workflowState.phase,
|
|
1571
1623
|
artifactsDir: workflowState.artifactsDir,
|
|
1572
|
-
openTasks: workflowState.openTasks,
|
|
1624
|
+
openTasks: workflowState.openTasks ?? [],
|
|
1573
1625
|
skillIntents: workflowState.skillIntents,
|
|
1574
1626
|
} : null,
|
|
1575
1627
|
blockers,
|
|
@@ -1601,7 +1653,7 @@ const status = defineCommand({
|
|
|
1601
1653
|
for (const blocker of blockers)
|
|
1602
1654
|
console.log(` - ${blocker}`);
|
|
1603
1655
|
}
|
|
1604
|
-
if (result.workflowState?.openTasks.length) {
|
|
1656
|
+
if ((result.workflowState?.openTasks.length ?? 0) > 0) {
|
|
1605
1657
|
console.log('\nOpen Tasks:');
|
|
1606
1658
|
for (const task of result.workflowState.openTasks)
|
|
1607
1659
|
console.log(` - ${task}`);
|
|
@@ -1704,8 +1756,8 @@ const init = defineCommand({
|
|
|
1704
1756
|
console.log(` Data dir: ${result.scaleDir}`);
|
|
1705
1757
|
console.log(` Scenario: ${scenarioMode}`);
|
|
1706
1758
|
console.log(`\n📋 Next steps:`);
|
|
1707
|
-
|
|
1708
|
-
|
|
1759
|
+
for (const step of governanceNextSteps())
|
|
1760
|
+
console.log(` → ${step}`);
|
|
1709
1761
|
return;
|
|
1710
1762
|
}
|
|
1711
1763
|
// One-click quick start mode
|
|
@@ -1777,7 +1829,7 @@ const init = defineCommand({
|
|
|
1777
1829
|
scaleDir: result.scaleDir,
|
|
1778
1830
|
created: result.created,
|
|
1779
1831
|
skipped: result.skipped,
|
|
1780
|
-
nextSteps:
|
|
1832
|
+
nextSteps: governanceNextSteps(),
|
|
1781
1833
|
}, null, 2));
|
|
1782
1834
|
return;
|
|
1783
1835
|
}
|
|
@@ -1794,8 +1846,8 @@ const init = defineCommand({
|
|
|
1794
1846
|
console.log(`\n📖 Knowledge: ${result.knowledgeDocPath}`);
|
|
1795
1847
|
console.log(`\n📂 Data dir: ${result.scaleDir}`);
|
|
1796
1848
|
console.log(`\n📋 Next steps:`);
|
|
1797
|
-
|
|
1798
|
-
|
|
1849
|
+
for (const step of governanceNextSteps())
|
|
1850
|
+
console.log(` → ${step}`);
|
|
1799
1851
|
},
|
|
1800
1852
|
});
|
|
1801
1853
|
// ============================================================================
|
|
@@ -2426,6 +2478,373 @@ const evidence = defineCommand({
|
|
|
2426
2478
|
subCommands: { list: evidenceList, show: evidenceShow },
|
|
2427
2479
|
});
|
|
2428
2480
|
// ============================================================================
|
|
2481
|
+
// runtime command - session ledger + completion evidence
|
|
2482
|
+
// ============================================================================
|
|
2483
|
+
function normalizeRuntimeEvidenceKind(value) {
|
|
2484
|
+
const normalized = String(value ?? 'command').trim();
|
|
2485
|
+
const allowed = ['command', 'gate', 'tool', 'skill', 'mcp', 'browser', 'desktop', 'manual', 'final-report'];
|
|
2486
|
+
if (allowed.includes(normalized))
|
|
2487
|
+
return normalized;
|
|
2488
|
+
throw new Error(`Invalid runtime evidence kind "${normalized}"; expected ${allowed.join(', ')}.`);
|
|
2489
|
+
}
|
|
2490
|
+
function normalizeRuntimeEvidenceStatus(value) {
|
|
2491
|
+
const normalized = String(value ?? '').trim();
|
|
2492
|
+
if (normalized === 'passed' || normalized === 'failed' || normalized === 'skipped')
|
|
2493
|
+
return normalized;
|
|
2494
|
+
throw new Error(`Invalid runtime evidence status "${normalized}"; expected passed, failed, or skipped.`);
|
|
2495
|
+
}
|
|
2496
|
+
function normalizeRuntimeSessionStatus(value) {
|
|
2497
|
+
const normalized = String(value ?? 'completed').trim();
|
|
2498
|
+
if (normalized === 'active' || normalized === 'completed' || normalized === 'failed' || normalized === 'abandoned')
|
|
2499
|
+
return normalized;
|
|
2500
|
+
throw new Error(`Invalid runtime session status "${normalized}"; expected active, completed, failed, or abandoned.`);
|
|
2501
|
+
}
|
|
2502
|
+
const runtimeStart = defineCommand({
|
|
2503
|
+
meta: { name: 'start', description: 'Start a runtime session ledger' },
|
|
2504
|
+
args: {
|
|
2505
|
+
'session-id': { type: 'string', description: 'Session id; generated when omitted' },
|
|
2506
|
+
'task-id': { type: 'string', description: 'Task id linked to this session' },
|
|
2507
|
+
agent: { type: 'string', description: 'Agent name' },
|
|
2508
|
+
level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
|
|
2509
|
+
summary: { type: 'string', description: 'Short session summary' },
|
|
2510
|
+
json: { type: 'boolean', default: false },
|
|
2511
|
+
},
|
|
2512
|
+
run({ args }) {
|
|
2513
|
+
const ledger = new SessionLedger({ projectDir: PROJECT_DIR, scaleDir: SCALE_DIR });
|
|
2514
|
+
const session = ledger.start({
|
|
2515
|
+
sessionId: args['session-id'],
|
|
2516
|
+
taskId: args['task-id'],
|
|
2517
|
+
agent: args.agent,
|
|
2518
|
+
level: normalizeTaskArtifactLevel(args.level),
|
|
2519
|
+
summary: args.summary,
|
|
2520
|
+
});
|
|
2521
|
+
if (args.json) {
|
|
2522
|
+
console.log(JSON.stringify(session, null, 2));
|
|
2523
|
+
return;
|
|
2524
|
+
}
|
|
2525
|
+
console.log(`Runtime session started: ${session.sessionId}`);
|
|
2526
|
+
if (session.taskId)
|
|
2527
|
+
console.log(` Task: ${session.taskId}`);
|
|
2528
|
+
if (session.level)
|
|
2529
|
+
console.log(` Level: ${session.level}`);
|
|
2530
|
+
console.log(` Events: ${ledger.sessionFile(session.sessionId)}`);
|
|
2531
|
+
},
|
|
2532
|
+
});
|
|
2533
|
+
const runtimeEnd = defineCommand({
|
|
2534
|
+
meta: { name: 'end', description: 'End the current or named runtime session' },
|
|
2535
|
+
args: {
|
|
2536
|
+
'session-id': { type: 'string', description: 'Session id; current session is used when omitted' },
|
|
2537
|
+
status: { type: 'string', default: 'completed', description: 'completed, failed, or abandoned' },
|
|
2538
|
+
summary: { type: 'string', description: 'Completion summary' },
|
|
2539
|
+
json: { type: 'boolean', default: false },
|
|
2540
|
+
},
|
|
2541
|
+
run({ args }) {
|
|
2542
|
+
const ledger = new SessionLedger({ projectDir: PROJECT_DIR, scaleDir: SCALE_DIR });
|
|
2543
|
+
const sessionId = args['session-id'] ?? ledger.current()?.sessionId;
|
|
2544
|
+
if (!sessionId) {
|
|
2545
|
+
console.error('No runtime session id provided and no current runtime session exists.');
|
|
2546
|
+
process.exit(1);
|
|
2547
|
+
}
|
|
2548
|
+
const session = ledger.end(sessionId, normalizeRuntimeSessionStatus(args.status), args.summary);
|
|
2549
|
+
if (args.json) {
|
|
2550
|
+
console.log(JSON.stringify(session, null, 2));
|
|
2551
|
+
return;
|
|
2552
|
+
}
|
|
2553
|
+
console.log(`Runtime session ended: ${session.sessionId}`);
|
|
2554
|
+
console.log(` Status: ${session.status}`);
|
|
2555
|
+
},
|
|
2556
|
+
});
|
|
2557
|
+
const runtimeRecord = defineCommand({
|
|
2558
|
+
meta: { name: 'record', description: 'Record command, gate, tool, browser, skill, or manual runtime evidence' },
|
|
2559
|
+
args: {
|
|
2560
|
+
'task-id': { type: 'string', description: 'Task id linked to this evidence' },
|
|
2561
|
+
'session-id': { type: 'string', description: 'Session id linked to this evidence' },
|
|
2562
|
+
kind: { type: 'string', default: 'command', description: 'command, gate, tool, skill, mcp, browser, desktop, manual, final-report' },
|
|
2563
|
+
title: { type: 'string', required: true, description: 'Evidence title' },
|
|
2564
|
+
status: { type: 'string', required: true, description: 'passed, failed, or skipped' },
|
|
2565
|
+
command: { type: 'string', description: 'Exact command or tool invocation, with secrets redacted by SCALE' },
|
|
2566
|
+
'exit-code': { type: 'string', description: 'Exit code when applicable' },
|
|
2567
|
+
summary: { type: 'string', required: true, description: 'Short output summary' },
|
|
2568
|
+
artifacts: { type: 'string', description: 'Comma-separated artifact paths' },
|
|
2569
|
+
'metadata-json': { type: 'string', default: '{}', description: 'Additional JSON metadata' },
|
|
2570
|
+
json: { type: 'boolean', default: false },
|
|
2571
|
+
},
|
|
2572
|
+
run({ args }) {
|
|
2573
|
+
const current = new SessionLedger({ projectDir: PROJECT_DIR, scaleDir: SCALE_DIR }).current();
|
|
2574
|
+
let metadata = {};
|
|
2575
|
+
try {
|
|
2576
|
+
metadata = JSON.parse(String(args['metadata-json'] ?? '{}'));
|
|
2577
|
+
}
|
|
2578
|
+
catch {
|
|
2579
|
+
console.error('--metadata-json must be valid JSON.');
|
|
2580
|
+
process.exit(1);
|
|
2581
|
+
}
|
|
2582
|
+
const exitCode = args['exit-code'] === undefined || args['exit-code'] === ''
|
|
2583
|
+
? undefined
|
|
2584
|
+
: Number.parseInt(String(args['exit-code']), 10);
|
|
2585
|
+
if (exitCode !== undefined && Number.isNaN(exitCode)) {
|
|
2586
|
+
console.error('--exit-code must be a number.');
|
|
2587
|
+
process.exit(1);
|
|
2588
|
+
}
|
|
2589
|
+
const ledger = new RuntimeEvidenceLedger({ projectDir: PROJECT_DIR, scaleDir: SCALE_DIR });
|
|
2590
|
+
const record = ledger.record({
|
|
2591
|
+
taskId: args['task-id'] ?? current?.taskId,
|
|
2592
|
+
sessionId: args['session-id'] ?? current?.sessionId,
|
|
2593
|
+
kind: normalizeRuntimeEvidenceKind(args.kind),
|
|
2594
|
+
title: args.title,
|
|
2595
|
+
status: normalizeRuntimeEvidenceStatus(args.status),
|
|
2596
|
+
command: args.command,
|
|
2597
|
+
exitCode,
|
|
2598
|
+
summary: args.summary,
|
|
2599
|
+
artifacts: parseCommaList(args.artifacts),
|
|
2600
|
+
metadata,
|
|
2601
|
+
});
|
|
2602
|
+
if (record.sessionId) {
|
|
2603
|
+
new SessionLedger({ projectDir: PROJECT_DIR, scaleDir: SCALE_DIR }).append(record.sessionId, {
|
|
2604
|
+
type: 'evidence.recorded',
|
|
2605
|
+
message: `${record.status}: ${record.title}`,
|
|
2606
|
+
data: {
|
|
2607
|
+
evidenceId: record.id,
|
|
2608
|
+
kind: record.kind,
|
|
2609
|
+
taskId: record.taskId,
|
|
2610
|
+
},
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
if (args.json) {
|
|
2614
|
+
console.log(JSON.stringify(record, null, 2));
|
|
2615
|
+
return;
|
|
2616
|
+
}
|
|
2617
|
+
console.log(`Runtime evidence recorded: ${record.id}`);
|
|
2618
|
+
console.log(` Status: ${record.status}`);
|
|
2619
|
+
console.log(` Kind: ${record.kind}`);
|
|
2620
|
+
if (record.redactionApplied)
|
|
2621
|
+
console.log(' Redaction: applied');
|
|
2622
|
+
},
|
|
2623
|
+
});
|
|
2624
|
+
const runtimeDoctor = defineCommand({
|
|
2625
|
+
meta: { name: 'doctor', description: 'Check runtime session and completion evidence' },
|
|
2626
|
+
args: {
|
|
2627
|
+
'task-id': { type: 'string', description: 'Task id to inspect' },
|
|
2628
|
+
'session-id': { type: 'string', description: 'Session id to inspect' },
|
|
2629
|
+
level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
|
|
2630
|
+
json: { type: 'boolean', default: false },
|
|
2631
|
+
},
|
|
2632
|
+
run({ args }) {
|
|
2633
|
+
const report = doctorRuntimeEvidence({
|
|
2634
|
+
projectDir: PROJECT_DIR,
|
|
2635
|
+
scaleDir: SCALE_DIR,
|
|
2636
|
+
taskId: args['task-id'],
|
|
2637
|
+
sessionId: args['session-id'],
|
|
2638
|
+
level: normalizeTaskArtifactLevel(args.level),
|
|
2639
|
+
});
|
|
2640
|
+
if (args.json) {
|
|
2641
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2642
|
+
if (report.blocked)
|
|
2643
|
+
process.exitCode = 1;
|
|
2644
|
+
return;
|
|
2645
|
+
}
|
|
2646
|
+
console.log('\nSCALE Runtime Doctor');
|
|
2647
|
+
console.log(` Evidence: ${report.evidence.total} total, ${report.evidence.passed} passed, ${report.evidence.failed} failed, ${report.evidence.skipped} skipped`);
|
|
2648
|
+
for (const check of report.checks) {
|
|
2649
|
+
console.log(` [${check.status.toUpperCase()}] ${check.name}: ${check.message}`);
|
|
2650
|
+
if (check.fix)
|
|
2651
|
+
console.log(` Fix: ${check.fix}`);
|
|
2652
|
+
}
|
|
2653
|
+
if (report.blocked)
|
|
2654
|
+
process.exitCode = 1;
|
|
2655
|
+
},
|
|
2656
|
+
});
|
|
2657
|
+
const runtimeFinalCheck = defineCommand({
|
|
2658
|
+
meta: { name: 'final-check', description: 'Block final delivery claims without passed runtime evidence' },
|
|
2659
|
+
args: {
|
|
2660
|
+
'task-id': { type: 'string', description: 'Task id to inspect' },
|
|
2661
|
+
'session-id': { type: 'string', description: 'Session id to inspect' },
|
|
2662
|
+
level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
|
|
2663
|
+
json: { type: 'boolean', default: false },
|
|
2664
|
+
},
|
|
2665
|
+
run({ args }) {
|
|
2666
|
+
const readiness = evaluateFinalReportReadiness({
|
|
2667
|
+
projectDir: PROJECT_DIR,
|
|
2668
|
+
scaleDir: SCALE_DIR,
|
|
2669
|
+
taskId: args['task-id'],
|
|
2670
|
+
sessionId: args['session-id'],
|
|
2671
|
+
level: normalizeTaskArtifactLevel(args.level),
|
|
2672
|
+
});
|
|
2673
|
+
if (args.json) {
|
|
2674
|
+
console.log(JSON.stringify(readiness, null, 2));
|
|
2675
|
+
if (readiness.blocked)
|
|
2676
|
+
process.exitCode = 1;
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
console.log('\nSCALE Runtime Final Check');
|
|
2680
|
+
console.log(` Ready: ${readiness.ready}`);
|
|
2681
|
+
for (const reason of readiness.reasons)
|
|
2682
|
+
console.log(` [BLOCKER] ${reason}`);
|
|
2683
|
+
if (readiness.blocked)
|
|
2684
|
+
process.exitCode = 1;
|
|
2685
|
+
},
|
|
2686
|
+
});
|
|
2687
|
+
const runtime = defineCommand({
|
|
2688
|
+
meta: { name: 'runtime', description: 'Runtime session ledger and completion evidence governance' },
|
|
2689
|
+
subCommands: {
|
|
2690
|
+
start: runtimeStart,
|
|
2691
|
+
end: runtimeEnd,
|
|
2692
|
+
record: runtimeRecord,
|
|
2693
|
+
doctor: runtimeDoctor,
|
|
2694
|
+
'final-check': runtimeFinalCheck,
|
|
2695
|
+
},
|
|
2696
|
+
});
|
|
2697
|
+
// ============================================================================
|
|
2698
|
+
// memory command - runtime evidence + knowledge + graph context packs
|
|
2699
|
+
// ============================================================================
|
|
2700
|
+
function parseMemoryBudget(value) {
|
|
2701
|
+
if (value === undefined || value === null || value === '')
|
|
2702
|
+
return undefined;
|
|
2703
|
+
const parsed = Number.parseInt(String(value), 10);
|
|
2704
|
+
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
2705
|
+
throw new Error('--budget must be a positive integer.');
|
|
2706
|
+
}
|
|
2707
|
+
return parsed;
|
|
2708
|
+
}
|
|
2709
|
+
const memoryPack = defineCommand({
|
|
2710
|
+
meta: { name: 'pack', description: 'Build a compact context pack from runtime evidence, session events, knowledge, and graph status' },
|
|
2711
|
+
args: {
|
|
2712
|
+
task: { type: 'string', required: true, description: 'Current task or question' },
|
|
2713
|
+
'task-id': { type: 'string', description: 'Task id to scope evidence and session data' },
|
|
2714
|
+
'session-id': { type: 'string', description: 'Session id to scope session events' },
|
|
2715
|
+
level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
|
|
2716
|
+
files: { type: 'string', description: 'Comma-separated files or modules in scope' },
|
|
2717
|
+
budget: { type: 'string', description: 'Maximum estimated tokens for the context pack' },
|
|
2718
|
+
json: { type: 'boolean', default: false },
|
|
2719
|
+
},
|
|
2720
|
+
async run({ args }) {
|
|
2721
|
+
let budgetTokens;
|
|
2722
|
+
try {
|
|
2723
|
+
budgetTokens = parseMemoryBudget(args.budget);
|
|
2724
|
+
}
|
|
2725
|
+
catch (e) {
|
|
2726
|
+
console.error(e.message);
|
|
2727
|
+
process.exit(1);
|
|
2728
|
+
}
|
|
2729
|
+
const { kb } = getEngine();
|
|
2730
|
+
const pack = await new MemoryFabric({
|
|
2731
|
+
projectDir: PROJECT_DIR,
|
|
2732
|
+
scaleDir: SCALE_DIR,
|
|
2733
|
+
knowledgeBase: kb,
|
|
2734
|
+
}).createContextPack({
|
|
2735
|
+
task: args.task,
|
|
2736
|
+
taskId: args['task-id'],
|
|
2737
|
+
sessionId: args['session-id'],
|
|
2738
|
+
level: normalizeTaskArtifactLevel(args.level),
|
|
2739
|
+
files: parseCommaList(args.files),
|
|
2740
|
+
budgetTokens,
|
|
2741
|
+
});
|
|
2742
|
+
if (args.json) {
|
|
2743
|
+
console.log(JSON.stringify(pack, null, 2));
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
console.log(renderContextPackMarkdown(pack));
|
|
2747
|
+
},
|
|
2748
|
+
});
|
|
2749
|
+
const memoryDoctor = defineCommand({
|
|
2750
|
+
meta: { name: 'doctor', description: 'Check whether a task context pack is available and within token budget' },
|
|
2751
|
+
args: {
|
|
2752
|
+
task: { type: 'string', required: true, description: 'Current task or question' },
|
|
2753
|
+
'task-id': { type: 'string', description: 'Task id to scope evidence and session data' },
|
|
2754
|
+
'session-id': { type: 'string', description: 'Session id to scope session events' },
|
|
2755
|
+
level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
|
|
2756
|
+
files: { type: 'string', description: 'Comma-separated files or modules in scope' },
|
|
2757
|
+
budget: { type: 'string', description: 'Maximum estimated tokens for the context pack' },
|
|
2758
|
+
json: { type: 'boolean', default: false },
|
|
2759
|
+
},
|
|
2760
|
+
async run({ args }) {
|
|
2761
|
+
let budgetTokens;
|
|
2762
|
+
try {
|
|
2763
|
+
budgetTokens = parseMemoryBudget(args.budget);
|
|
2764
|
+
}
|
|
2765
|
+
catch (e) {
|
|
2766
|
+
console.error(e.message);
|
|
2767
|
+
process.exit(1);
|
|
2768
|
+
}
|
|
2769
|
+
const { kb } = getEngine();
|
|
2770
|
+
const report = await doctorMemoryFabric({
|
|
2771
|
+
projectDir: PROJECT_DIR,
|
|
2772
|
+
scaleDir: SCALE_DIR,
|
|
2773
|
+
knowledgeBase: kb,
|
|
2774
|
+
}, {
|
|
2775
|
+
task: args.task,
|
|
2776
|
+
taskId: args['task-id'],
|
|
2777
|
+
sessionId: args['session-id'],
|
|
2778
|
+
level: normalizeTaskArtifactLevel(args.level),
|
|
2779
|
+
files: parseCommaList(args.files),
|
|
2780
|
+
budgetTokens,
|
|
2781
|
+
});
|
|
2782
|
+
if (args.json) {
|
|
2783
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2784
|
+
if (!report.ok)
|
|
2785
|
+
process.exitCode = 1;
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2788
|
+
console.log('\nSCALE Memory Doctor');
|
|
2789
|
+
console.log(` Budget: ${report.pack.budget.used}/${report.pack.budget.limit} estimated tokens`);
|
|
2790
|
+
for (const check of report.checks) {
|
|
2791
|
+
console.log(` [${check.status.toUpperCase()}] ${check.name}: ${check.message}`);
|
|
2792
|
+
}
|
|
2793
|
+
if (!report.ok)
|
|
2794
|
+
process.exitCode = 1;
|
|
2795
|
+
},
|
|
2796
|
+
});
|
|
2797
|
+
const memorySettle = defineCommand({
|
|
2798
|
+
meta: { name: 'settle', description: 'Settle runtime evidence into a reviewable memory learning candidate' },
|
|
2799
|
+
args: {
|
|
2800
|
+
task: { type: 'string', required: true, description: 'Current task or question' },
|
|
2801
|
+
'task-id': { type: 'string', description: 'Task id to scope evidence and session data' },
|
|
2802
|
+
'session-id': { type: 'string', description: 'Session id to scope session events' },
|
|
2803
|
+
level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
|
|
2804
|
+
files: { type: 'string', description: 'Comma-separated files or modules in scope' },
|
|
2805
|
+
budget: { type: 'string', description: 'Maximum estimated tokens for the context pack' },
|
|
2806
|
+
json: { type: 'boolean', default: false },
|
|
2807
|
+
},
|
|
2808
|
+
async run({ args }) {
|
|
2809
|
+
let budgetTokens;
|
|
2810
|
+
try {
|
|
2811
|
+
budgetTokens = parseMemoryBudget(args.budget);
|
|
2812
|
+
}
|
|
2813
|
+
catch (e) {
|
|
2814
|
+
console.error(e.message);
|
|
2815
|
+
process.exit(1);
|
|
2816
|
+
}
|
|
2817
|
+
const { kb } = getEngine();
|
|
2818
|
+
const pack = await new MemoryFabric({
|
|
2819
|
+
projectDir: PROJECT_DIR,
|
|
2820
|
+
scaleDir: SCALE_DIR,
|
|
2821
|
+
knowledgeBase: kb,
|
|
2822
|
+
}).createContextPack({
|
|
2823
|
+
task: args.task,
|
|
2824
|
+
taskId: args['task-id'],
|
|
2825
|
+
sessionId: args['session-id'],
|
|
2826
|
+
level: normalizeTaskArtifactLevel(args.level),
|
|
2827
|
+
files: parseCommaList(args.files),
|
|
2828
|
+
budgetTokens,
|
|
2829
|
+
});
|
|
2830
|
+
const settlement = settleMemoryLearning({
|
|
2831
|
+
projectDir: PROJECT_DIR,
|
|
2832
|
+
scaleDir: SCALE_DIR,
|
|
2833
|
+
pack,
|
|
2834
|
+
});
|
|
2835
|
+
if (args.json) {
|
|
2836
|
+
console.log(JSON.stringify(settlement, null, 2));
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
console.log(renderMemoryLearningCandidateMarkdown(settlement.candidate));
|
|
2840
|
+
console.log(`\nWrote: ${settlement.files.markdown}`);
|
|
2841
|
+
},
|
|
2842
|
+
});
|
|
2843
|
+
const memory = defineCommand({
|
|
2844
|
+
meta: { name: 'memory', description: 'Memory Fabric context packs and budget diagnostics' },
|
|
2845
|
+
subCommands: { pack: memoryPack, doctor: memoryDoctor, settle: memorySettle },
|
|
2846
|
+
});
|
|
2847
|
+
// ============================================================================
|
|
2429
2848
|
// out-of-scope command — 借鉴 mattpocock/skills 的 .out-of-scope/ 设计
|
|
2430
2849
|
// ============================================================================
|
|
2431
2850
|
const outOfScopeAdd = defineCommand({
|
|
@@ -3220,6 +3639,8 @@ const main = defineCommand({
|
|
|
3220
3639
|
status,
|
|
3221
3640
|
workflow,
|
|
3222
3641
|
evidence,
|
|
3642
|
+
runtime,
|
|
3643
|
+
memory,
|
|
3223
3644
|
diagnose,
|
|
3224
3645
|
tdd,
|
|
3225
3646
|
tool,
|