scene-capability-engine 3.6.38 → 3.6.44
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 +63 -0
- package/bin/scene-capability-engine.js +42 -2
- package/docs/command-reference.md +27 -0
- package/docs/developer-guide.md +1 -1
- package/docs/document-governance.md +22 -2
- package/docs/releases/README.md +6 -0
- package/docs/releases/v3.6.39.md +24 -0
- package/docs/releases/v3.6.40.md +19 -0
- package/docs/releases/v3.6.41.md +20 -0
- package/docs/releases/v3.6.42.md +19 -0
- package/docs/releases/v3.6.43.md +17 -0
- package/docs/releases/v3.6.44.md +17 -0
- package/docs/spec-collaboration-guide.md +1 -1
- package/docs/state-migration-reconciliation-runbook.md +76 -0
- package/docs/state-storage-tiering.md +104 -0
- package/docs/zh/releases/README.md +6 -0
- package/docs/zh/releases/v3.6.39.md +24 -0
- package/docs/zh/releases/v3.6.40.md +19 -0
- package/docs/zh/releases/v3.6.41.md +20 -0
- package/docs/zh/releases/v3.6.42.md +19 -0
- package/docs/zh/releases/v3.6.43.md +17 -0
- package/docs/zh/releases/v3.6.44.md +17 -0
- package/lib/adoption/adoption-logger.js +1 -1
- package/lib/adoption/adoption-strategy.js +29 -29
- package/lib/adoption/detection-engine.js +16 -13
- package/lib/adoption/smart-orchestrator.js +3 -3
- package/lib/adoption/strategy-selector.js +19 -15
- package/lib/adoption/template-sync.js +3 -3
- package/lib/auto/autonomous-engine.js +5 -5
- package/lib/auto/handoff-release-gate-history-loaders-service.js +24 -4
- package/lib/auto/handoff-run-service.js +37 -0
- package/lib/backup/backup-system.js +10 -10
- package/lib/collab/collab-manager.js +8 -5
- package/lib/collab/dependency-manager.js +1 -1
- package/lib/commands/adopt.js +2 -2
- package/lib/commands/auto.js +239 -97
- package/lib/commands/collab.js +10 -4
- package/lib/commands/docs.js +8 -2
- package/lib/commands/scene.js +78 -18
- package/lib/commands/status.js +3 -3
- package/lib/commands/studio.js +8 -0
- package/lib/commands/watch.js +10 -1
- package/lib/governance/config-manager.js +16 -0
- package/lib/governance/diagnostic-engine.js +2 -1
- package/lib/governance/validation-engine.js +3 -2
- package/lib/repo/config-manager.js +2 -2
- package/lib/runtime/session-store.js +8 -0
- package/lib/spec/bootstrap/context-collector.js +5 -4
- package/lib/spec-gate/rules/default-rules.js +8 -8
- package/lib/state/sce-state-store.js +265 -0
- package/lib/state/state-migration-manager.js +27 -2
- package/lib/state/state-storage-policy.js +179 -0
- package/lib/upgrade/migration-engine.js +5 -5
- package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +3 -3
- package/lib/utils/tool-detector.js +4 -4
- package/lib/utils/validation.js +6 -6
- package/lib/watch/action-executor.js +10 -1
- package/lib/watch/event-debouncer.js +3 -0
- package/lib/watch/file-watcher.js +51 -10
- package/lib/watch/watch-manager.js +10 -1
- package/lib/workspace/multi/workspace-context-resolver.js +3 -3
- package/lib/workspace/multi/workspace-registry.js +3 -3
- package/lib/workspace/multi/workspace-state-manager.js +3 -3
- package/lib/workspace/spec-delivery-audit.js +553 -0
- package/lib/workspace/takeover-baseline.js +11 -0
- package/package.json +5 -1
- package/template/.sce/config/state-storage-policy.json +165 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# v3.6.41 发布说明
|
|
2
|
+
|
|
3
|
+
发布日期:2026-03-13
|
|
4
|
+
|
|
5
|
+
## 重点变化
|
|
6
|
+
|
|
7
|
+
- 完成了一轮务实的 SCE 接管清理,仓库当前已经与 takeover baseline 对齐,活跃代码路径的命名也统一回到了 SCE。
|
|
8
|
+
- 修复协作分配元数据写入逻辑,新写入统一持久化 `sceInstance`,同时继续兼容读取旧的 `kiroInstance` 元数据。
|
|
9
|
+
- 清理了备份、adoption、bootstrap、spec gate、auto runtime、repo config、CLI 初始化等活跃模块中的高价值 legacy 命名漂移,但没有破坏刻意保留的 `.kiro` 迁移兼容层。
|
|
10
|
+
|
|
11
|
+
## 验证
|
|
12
|
+
|
|
13
|
+
- `npx jest tests/unit/collab/collab-manager.test.js tests/unit/adoption/strategy-selector.test.js tests/unit/adoption/adoption-logger.test.js tests/unit/adoption.test.js --runInBand`
|
|
14
|
+
- `node bin/sce.js workspace takeover-audit --json`
|
|
15
|
+
|
|
16
|
+
## 发布说明
|
|
17
|
+
|
|
18
|
+
- 这次发布刻意只做“有价值清理”,不是全仓盲目重命名。真正的 `.kiro` legacy migration 支持仍然保留。
|
|
19
|
+
- 运行时规范命名现在以 `hasSceDir` 和 `sceInstance` 为准;对历史项目状态和旧元数据所需的兼容别名仍然继续支持。
|
|
20
|
+
- 仓库内并不存在实际的 `.kiro-workspaces` 运行时目录遗留;本次交付主要消化的是活跃命名债和一个真实的协作元数据写入缺陷。
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# v3.6.42 发布说明
|
|
2
|
+
|
|
3
|
+
发布日期:2026-03-13
|
|
4
|
+
|
|
5
|
+
## 重点变化
|
|
6
|
+
|
|
7
|
+
- 加固了 handoff release preflight:当发布过程里 `release-gate-history.json` 临时不可用,但当前 `release-gate-*.json` 已生成时,发布仍可继续判定。
|
|
8
|
+
- 提升了 release-gate history 加载的容错性,单个异常 report 或 seed entry 现在会被告警跳过,而不是直接打断整个 gate-index 构建。
|
|
9
|
+
- 补强了发布工作流诊断信息:当 history index 生成失败时,会把 `gate-index` 的 stderr 一并落到 release evidence 中。
|
|
10
|
+
|
|
11
|
+
## 验证
|
|
12
|
+
|
|
13
|
+
- `npx jest tests/unit/commands/auto.test.js --runInBand --testNamePattern "preflight-check|falls back to latest release gate report when history index is empty"`
|
|
14
|
+
- `npx jest tests/unit/auto/handoff-release-gate-history-loaders-service.test.js --runInBand`
|
|
15
|
+
|
|
16
|
+
## 发布说明
|
|
17
|
+
|
|
18
|
+
- 这个补丁版直接针对 `v3.6.41` 发布工作流失败链路,重点修复的是 history index 构建失败后又把 publish 阻断的路径。
|
|
19
|
+
- 发布门槛本身没有放松,只是在当前 gate report 已经存在时提供了安全回退,并补上真实上游错误的诊断暴露。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# v3.6.43 发布说明
|
|
2
|
+
|
|
3
|
+
发布日期:2026-03-13
|
|
4
|
+
|
|
5
|
+
## 重点变化
|
|
6
|
+
|
|
7
|
+
- 修复了 `v3.6.42` 暴露出的第二层发布阻断问题:当 drift 只有 advisory alert、没有 `drift-blocked runs` 时,不再让 `Enforce handoff preflight hard gate` 失败。
|
|
8
|
+
- 再次补强了发布工作流可观测性:如果 preflight gate 失败,现在会把生成出来的 JSON 直接打印到 Actions 日志里。
|
|
9
|
+
|
|
10
|
+
## 验证
|
|
11
|
+
|
|
12
|
+
- `npx jest tests/unit/commands/auto.test.js --runInBand --testNamePattern "preflight-check|advisory drift alerts without blocked runs|falls back to latest release gate report when history index is empty"`
|
|
13
|
+
|
|
14
|
+
## 发布说明
|
|
15
|
+
|
|
16
|
+
- `v3.6.42` 修的是 history 缺失链路,但同时暴露出另一个策略缺陷:advisory drift alert 被误当成了 hard blocker。
|
|
17
|
+
- `v3.6.43` 把 hard gate 收敛回真正应该阻断发布的条件:最新 release gate 失败,或 history 中存在 drift-blocked runs。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# v3.6.44 发布说明
|
|
2
|
+
|
|
3
|
+
发布日期:2026-03-13
|
|
4
|
+
|
|
5
|
+
## 重点变化
|
|
6
|
+
|
|
7
|
+
- 修复了 GitHub Actions tag 发布工作流中的最后一个阻断点:当声明交付文件都已被跟踪且工作区干净时,spec delivery sync 现在允许 detached `HEAD` 的 release checkout 通过。
|
|
8
|
+
|
|
9
|
+
## 验证
|
|
10
|
+
|
|
11
|
+
- `npx jest tests/unit/workspace/spec-delivery-audit.test.js --runInBand`
|
|
12
|
+
- `npx jest tests/unit/commands/auto.test.js --runInBand --testNamePattern "preflight-check|advisory drift alerts without blocked runs|falls back to latest release gate report when history index is empty"`
|
|
13
|
+
|
|
14
|
+
## 发布说明
|
|
15
|
+
|
|
16
|
+
- 这个补丁版专门针对 GitHub Actions 的 `v*` tag push 发布场景;该场景天然是 detached `HEAD`,本来就不具备 branch upstream tracking。
|
|
17
|
+
- 声明文件是否已跟踪、工作区是否干净、是否缺文件这些真正的交付完整性校验仍然继续强制执行;放开的只是该发布环境下无意义的 upstream 证明。
|
|
@@ -307,7 +307,7 @@ class AdoptionLogger {
|
|
|
307
307
|
*/
|
|
308
308
|
detectionResult(state) {
|
|
309
309
|
this.info('Project state detected', {
|
|
310
|
-
|
|
310
|
+
hasSceDir: state.hasSceDir !== undefined ? state.hasSceDir : state.hasKiroDir,
|
|
311
311
|
hasVersionFile: state.hasVersionFile,
|
|
312
312
|
currentVersion: state.currentVersion,
|
|
313
313
|
targetVersion: state.targetVersion,
|
|
@@ -34,7 +34,7 @@ class AdoptionStrategy {
|
|
|
34
34
|
* @param {string} projectPath - Absolute path to project root
|
|
35
35
|
* @returns {string}
|
|
36
36
|
*/
|
|
37
|
-
|
|
37
|
+
getScePath(projectPath) {
|
|
38
38
|
return path.join(projectPath, this.sceDir);
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -65,17 +65,17 @@ class AdoptionStrategy {
|
|
|
65
65
|
/**
|
|
66
66
|
* Creates initial directory structure
|
|
67
67
|
*
|
|
68
|
-
* @param {string}
|
|
68
|
+
* @param {string} scePath - Path to .sce/ directory
|
|
69
69
|
* @returns {Promise<void>}
|
|
70
70
|
*/
|
|
71
|
-
async createDirectoryStructure(
|
|
72
|
-
await ensureDirectory(
|
|
73
|
-
await ensureDirectory(path.join(
|
|
74
|
-
await ensureDirectory(path.join(
|
|
75
|
-
await ensureDirectory(path.join(
|
|
76
|
-
await ensureDirectory(path.join(
|
|
77
|
-
await ensureDirectory(path.join(
|
|
78
|
-
await ensureDirectory(path.join(
|
|
71
|
+
async createDirectoryStructure(scePath) {
|
|
72
|
+
await ensureDirectory(scePath);
|
|
73
|
+
await ensureDirectory(path.join(scePath, 'specs'));
|
|
74
|
+
await ensureDirectory(path.join(scePath, 'steering'));
|
|
75
|
+
await ensureDirectory(path.join(scePath, 'tools'));
|
|
76
|
+
await ensureDirectory(path.join(scePath, 'config'));
|
|
77
|
+
await ensureDirectory(path.join(scePath, 'backups'));
|
|
78
|
+
await ensureDirectory(path.join(scePath, 'hooks'));
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
@@ -90,7 +90,7 @@ class AdoptionStrategy {
|
|
|
90
90
|
*/
|
|
91
91
|
async copyTemplateFiles(projectPath, options = {}) {
|
|
92
92
|
const { overwrite = false, skip = [], resolutionMap = {} } = options;
|
|
93
|
-
const
|
|
93
|
+
const scePath = this.getScePath(projectPath);
|
|
94
94
|
const templatePath = this.getTemplatePath();
|
|
95
95
|
|
|
96
96
|
const created = [];
|
|
@@ -144,7 +144,7 @@ class AdoptionStrategy {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
const sourcePath = path.join(templatePath, file);
|
|
147
|
-
const destPath = path.join(
|
|
147
|
+
const destPath = path.join(scePath, file);
|
|
148
148
|
|
|
149
149
|
// Check if source exists
|
|
150
150
|
const sourceExists = await pathExists(sourcePath);
|
|
@@ -208,11 +208,11 @@ class FreshAdoption extends AdoptionStrategy {
|
|
|
208
208
|
const warnings = [];
|
|
209
209
|
|
|
210
210
|
try {
|
|
211
|
-
const
|
|
211
|
+
const scePath = this.getScePath(projectPath);
|
|
212
212
|
|
|
213
213
|
// Check if .sce/ already exists
|
|
214
|
-
const
|
|
215
|
-
if (
|
|
214
|
+
const sceExists = await pathExists(scePath);
|
|
215
|
+
if (sceExists) {
|
|
216
216
|
throw new Error('.sce/ directory already exists - use partial or full adoption');
|
|
217
217
|
}
|
|
218
218
|
|
|
@@ -230,7 +230,7 @@ class FreshAdoption extends AdoptionStrategy {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
// Create directory structure
|
|
233
|
-
await this.createDirectoryStructure(
|
|
233
|
+
await this.createDirectoryStructure(scePath);
|
|
234
234
|
filesCreated.push('.sce/');
|
|
235
235
|
filesCreated.push('.sce/specs/');
|
|
236
236
|
filesCreated.push('.sce/steering/');
|
|
@@ -299,16 +299,16 @@ class PartialAdoption extends AdoptionStrategy {
|
|
|
299
299
|
const warnings = [];
|
|
300
300
|
|
|
301
301
|
try {
|
|
302
|
-
const
|
|
302
|
+
const scePath = this.getScePath(projectPath);
|
|
303
303
|
|
|
304
304
|
// Check if .sce/ exists
|
|
305
|
-
const
|
|
306
|
-
if (!
|
|
305
|
+
const sceExists = await pathExists(scePath);
|
|
306
|
+
if (!sceExists) {
|
|
307
307
|
throw new Error('.sce/ directory does not exist - use fresh adoption');
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
// Check if version.json exists
|
|
311
|
-
const versionPath = path.join(
|
|
311
|
+
const versionPath = path.join(scePath, 'version.json');
|
|
312
312
|
const versionExists = await pathExists(versionPath);
|
|
313
313
|
if (versionExists) {
|
|
314
314
|
warnings.push('version.json already exists - use full adoption for upgrades');
|
|
@@ -328,12 +328,12 @@ class PartialAdoption extends AdoptionStrategy {
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
// Ensure all required directories exist
|
|
331
|
-
const specsPath = path.join(
|
|
332
|
-
const steeringPath = path.join(
|
|
333
|
-
const toolsPath = path.join(
|
|
334
|
-
const configPath = path.join(
|
|
335
|
-
const backupsPath = path.join(
|
|
336
|
-
const hooksPath = path.join(
|
|
331
|
+
const specsPath = path.join(scePath, 'specs');
|
|
332
|
+
const steeringPath = path.join(scePath, 'steering');
|
|
333
|
+
const toolsPath = path.join(scePath, 'tools');
|
|
334
|
+
const configPath = path.join(scePath, 'config');
|
|
335
|
+
const backupsPath = path.join(scePath, 'backups');
|
|
336
|
+
const hooksPath = path.join(scePath, 'hooks');
|
|
337
337
|
|
|
338
338
|
if (!await pathExists(specsPath)) {
|
|
339
339
|
await ensureDirectory(specsPath);
|
|
@@ -437,11 +437,11 @@ class FullAdoption extends AdoptionStrategy {
|
|
|
437
437
|
const warnings = [];
|
|
438
438
|
|
|
439
439
|
try {
|
|
440
|
-
const
|
|
440
|
+
const scePath = this.getScePath(projectPath);
|
|
441
441
|
|
|
442
442
|
// Check if .sce/ exists
|
|
443
|
-
const
|
|
444
|
-
if (!
|
|
443
|
+
const sceExists = await pathExists(scePath);
|
|
444
|
+
if (!sceExists) {
|
|
445
445
|
throw new Error('.sce/ directory does not exist - use fresh adoption');
|
|
446
446
|
}
|
|
447
447
|
|
|
@@ -31,8 +31,8 @@ class DetectionEngine {
|
|
|
31
31
|
*/
|
|
32
32
|
async analyze(projectPath) {
|
|
33
33
|
try {
|
|
34
|
-
const
|
|
35
|
-
const
|
|
34
|
+
const scePath = path.join(projectPath, this.sceDir);
|
|
35
|
+
const hasSceDir = await pathExists(scePath);
|
|
36
36
|
|
|
37
37
|
let hasVersionFile = false;
|
|
38
38
|
let hasSpecs = false;
|
|
@@ -41,9 +41,9 @@ class DetectionEngine {
|
|
|
41
41
|
let existingVersion = null;
|
|
42
42
|
let steeringDetection = null;
|
|
43
43
|
|
|
44
|
-
if (
|
|
44
|
+
if (hasSceDir) {
|
|
45
45
|
// Check for version.json
|
|
46
|
-
const versionPath = path.join(
|
|
46
|
+
const versionPath = path.join(scePath, this.versionFile);
|
|
47
47
|
hasVersionFile = await pathExists(versionPath);
|
|
48
48
|
|
|
49
49
|
if (hasVersionFile) {
|
|
@@ -57,7 +57,7 @@ class DetectionEngine {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// Check for specs/
|
|
60
|
-
const specsPath = path.join(
|
|
60
|
+
const specsPath = path.join(scePath, this.specsDir);
|
|
61
61
|
hasSpecs = await pathExists(specsPath);
|
|
62
62
|
|
|
63
63
|
// Check for steering/ using SteeringManager
|
|
@@ -65,7 +65,7 @@ class DetectionEngine {
|
|
|
65
65
|
hasSteering = steeringDetection.hasExistingSteering;
|
|
66
66
|
|
|
67
67
|
// Check for tools/
|
|
68
|
-
const toolsPath = path.join(
|
|
68
|
+
const toolsPath = path.join(scePath, this.toolsDir);
|
|
69
69
|
hasTools = await pathExists(toolsPath);
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -73,10 +73,11 @@ class DetectionEngine {
|
|
|
73
73
|
const projectType = await this.detectProjectType(projectPath);
|
|
74
74
|
|
|
75
75
|
// Detect conflicts (only if we're going to add template files)
|
|
76
|
-
const conflicts =
|
|
76
|
+
const conflicts = hasSceDir ? await this.detectConflicts(projectPath) : [];
|
|
77
77
|
|
|
78
78
|
return {
|
|
79
|
-
|
|
79
|
+
hasSceDir,
|
|
80
|
+
hasKiroDir: hasSceDir,
|
|
80
81
|
hasVersionFile,
|
|
81
82
|
hasSpecs,
|
|
82
83
|
hasSteering,
|
|
@@ -98,8 +99,9 @@ class DetectionEngine {
|
|
|
98
99
|
* @returns {AdoptionMode} - 'fresh', 'partial', or 'full'
|
|
99
100
|
*/
|
|
100
101
|
determineStrategy(result) {
|
|
102
|
+
const hasSceDir = result && result.hasSceDir !== undefined ? result.hasSceDir : result.hasKiroDir;
|
|
101
103
|
// Fresh adoption: no .sce/ directory
|
|
102
|
-
if (!
|
|
104
|
+
if (!hasSceDir) {
|
|
103
105
|
return 'fresh';
|
|
104
106
|
}
|
|
105
107
|
|
|
@@ -152,7 +154,7 @@ class DetectionEngine {
|
|
|
152
154
|
const conflicts = [];
|
|
153
155
|
|
|
154
156
|
try {
|
|
155
|
-
const
|
|
157
|
+
const scePath = path.join(projectPath, this.sceDir);
|
|
156
158
|
|
|
157
159
|
// Define template files that might conflict
|
|
158
160
|
const templateFiles = [
|
|
@@ -177,7 +179,7 @@ class DetectionEngine {
|
|
|
177
179
|
];
|
|
178
180
|
|
|
179
181
|
for (const templateFile of templateFiles) {
|
|
180
|
-
const filePath = path.join(
|
|
182
|
+
const filePath = path.join(scePath, templateFile);
|
|
181
183
|
const exists = await pathExists(filePath);
|
|
182
184
|
|
|
183
185
|
if (exists) {
|
|
@@ -249,12 +251,13 @@ class DetectionEngine {
|
|
|
249
251
|
*/
|
|
250
252
|
getSummary(result) {
|
|
251
253
|
const lines = [];
|
|
254
|
+
const hasSceDir = result && result.hasSceDir !== undefined ? result.hasSceDir : result.hasKiroDir;
|
|
252
255
|
|
|
253
256
|
lines.push('Project Analysis:');
|
|
254
257
|
lines.push(` Project Type: ${result.projectType}`);
|
|
255
|
-
lines.push(` .sce/ Directory: ${
|
|
258
|
+
lines.push(` .sce/ Directory: ${hasSceDir ? 'Yes' : 'No'}`);
|
|
256
259
|
|
|
257
|
-
if (
|
|
260
|
+
if (hasSceDir) {
|
|
258
261
|
lines.push(` version.json: ${result.hasVersionFile ? 'Yes' : 'No'}`);
|
|
259
262
|
if (result.existingVersion) {
|
|
260
263
|
lines.push(` Current Version: ${result.existingVersion}`);
|
|
@@ -317,10 +317,10 @@ class SmartOrchestrator {
|
|
|
317
317
|
|
|
318
318
|
} else if (mode === 'smart-adopt' || mode === 'smart-update') {
|
|
319
319
|
// Check which template files exist and differ
|
|
320
|
-
const
|
|
320
|
+
const scePath = path.join(projectPath, '.sce');
|
|
321
321
|
|
|
322
322
|
for (const templateFile of templateFiles) {
|
|
323
|
-
const filePath = path.join(
|
|
323
|
+
const filePath = path.join(scePath, templateFile);
|
|
324
324
|
const fs = require('fs-extra');
|
|
325
325
|
|
|
326
326
|
if (await fs.pathExists(filePath)) {
|
|
@@ -340,7 +340,7 @@ class SmartOrchestrator {
|
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
// Always preserve CURRENT_CONTEXT.md if it exists
|
|
343
|
-
const currentContextPath = path.join(
|
|
343
|
+
const currentContextPath = path.join(scePath, 'steering/CURRENT_CONTEXT.md');
|
|
344
344
|
const fs = require('fs-extra');
|
|
345
345
|
if (await fs.pathExists(currentContextPath)) {
|
|
346
346
|
plan.filesToPreserve.push('steering/CURRENT_CONTEXT.md');
|
|
@@ -31,7 +31,11 @@ const AdoptionMode = {
|
|
|
31
31
|
*/
|
|
32
32
|
class ProjectState {
|
|
33
33
|
constructor(data = {}) {
|
|
34
|
-
this.
|
|
34
|
+
this.hasSceDir = data.hasSceDir !== undefined
|
|
35
|
+
? data.hasSceDir
|
|
36
|
+
: (data.hasKiroDir !== undefined ? data.hasKiroDir : false);
|
|
37
|
+
// Backward-compatible alias for older callers/tests.
|
|
38
|
+
this.hasKiroDir = this.hasSceDir;
|
|
35
39
|
this.hasVersionFile = data.hasVersionFile !== undefined ? data.hasVersionFile : false;
|
|
36
40
|
this.currentVersion = data.currentVersion !== undefined ? data.currentVersion : null;
|
|
37
41
|
this.targetVersion = data.targetVersion !== undefined ? data.targetVersion : null;
|
|
@@ -61,22 +65,22 @@ class StrategySelector {
|
|
|
61
65
|
* @returns {Promise<ProjectState>} - Comprehensive project state
|
|
62
66
|
*/
|
|
63
67
|
async detectProjectState(projectPath) {
|
|
64
|
-
const
|
|
65
|
-
const versionPath = path.join(
|
|
66
|
-
const specsPath = path.join(
|
|
67
|
-
const steeringPath = path.join(
|
|
68
|
-
const toolsPath = path.join(
|
|
68
|
+
const scePath = path.join(projectPath, '.sce');
|
|
69
|
+
const versionPath = path.join(scePath, 'version.json');
|
|
70
|
+
const specsPath = path.join(scePath, 'specs');
|
|
71
|
+
const steeringPath = path.join(scePath, 'steering');
|
|
72
|
+
const toolsPath = path.join(scePath, 'tools');
|
|
69
73
|
|
|
70
74
|
// Get target version from package.json
|
|
71
75
|
const packageJson = require('../../package.json');
|
|
72
76
|
const targetVersion = packageJson.version;
|
|
73
77
|
|
|
74
78
|
// Check directory existence
|
|
75
|
-
const
|
|
76
|
-
const hasVersionFile =
|
|
77
|
-
const hasSpecs =
|
|
78
|
-
const hasSteering =
|
|
79
|
-
const hasTools =
|
|
79
|
+
const hasSceDir = await this.fs.pathExists(scePath);
|
|
80
|
+
const hasVersionFile = hasSceDir && await this.fs.pathExists(versionPath);
|
|
81
|
+
const hasSpecs = hasSceDir && await this.fs.pathExists(specsPath);
|
|
82
|
+
const hasSteering = hasSceDir && await this.fs.pathExists(steeringPath);
|
|
83
|
+
const hasTools = hasSceDir && await this.fs.pathExists(toolsPath);
|
|
80
84
|
|
|
81
85
|
// Read current version if available
|
|
82
86
|
let currentVersion = null;
|
|
@@ -103,7 +107,7 @@ class StrategySelector {
|
|
|
103
107
|
|
|
104
108
|
// Detect potential conflicts (files that exist and might be overwritten)
|
|
105
109
|
const conflicts = [];
|
|
106
|
-
if (
|
|
110
|
+
if (hasSceDir) {
|
|
107
111
|
const templateFiles = [
|
|
108
112
|
'steering/CORE_PRINCIPLES.md',
|
|
109
113
|
'steering/ENVIRONMENT.md',
|
|
@@ -116,7 +120,7 @@ class StrategySelector {
|
|
|
116
120
|
];
|
|
117
121
|
|
|
118
122
|
for (const templateFile of templateFiles) {
|
|
119
|
-
const filePath = path.join(
|
|
123
|
+
const filePath = path.join(scePath, templateFile);
|
|
120
124
|
if (await this.fs.pathExists(filePath)) {
|
|
121
125
|
conflicts.push(templateFile);
|
|
122
126
|
}
|
|
@@ -124,7 +128,7 @@ class StrategySelector {
|
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
return new ProjectState({
|
|
127
|
-
|
|
131
|
+
hasSceDir,
|
|
128
132
|
hasVersionFile,
|
|
129
133
|
currentVersion,
|
|
130
134
|
targetVersion,
|
|
@@ -144,7 +148,7 @@ class StrategySelector {
|
|
|
144
148
|
*/
|
|
145
149
|
selectMode(state) {
|
|
146
150
|
// No .sce/ directory - fresh adoption
|
|
147
|
-
if (!state.
|
|
151
|
+
if (!state.hasSceDir) {
|
|
148
152
|
return AdoptionMode.FRESH;
|
|
149
153
|
}
|
|
150
154
|
|
|
@@ -59,7 +59,7 @@ class TemplateSync {
|
|
|
59
59
|
errors: [] // Files that couldn't be compared
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
const
|
|
62
|
+
const scePath = path.join(projectPath, '.sce');
|
|
63
63
|
|
|
64
64
|
for (const templateFile of this.templateFiles) {
|
|
65
65
|
try {
|
|
@@ -72,7 +72,7 @@ class TemplateSync {
|
|
|
72
72
|
continue;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
const projectFilePath = path.join(
|
|
75
|
+
const projectFilePath = path.join(scePath, templateFile);
|
|
76
76
|
const templateFilePath = path.join(templatePath, templateFile);
|
|
77
77
|
|
|
78
78
|
// Check if template file exists
|
|
@@ -323,7 +323,7 @@ class TemplateSync {
|
|
|
323
323
|
|
|
324
324
|
const synced = [];
|
|
325
325
|
const errors = [];
|
|
326
|
-
const
|
|
326
|
+
const scePath = path.join(projectPath, '.sce');
|
|
327
327
|
|
|
328
328
|
// Sync missing files
|
|
329
329
|
for (const missing of report.differences.missing) {
|
|
@@ -69,25 +69,25 @@ class AutonomousEngine {
|
|
|
69
69
|
const fs = require('fs-extra');
|
|
70
70
|
|
|
71
71
|
// Check for .sce directory (接管标志)
|
|
72
|
-
const
|
|
73
|
-
if (!await fs.pathExists(
|
|
72
|
+
const sceDir = path.join(process.cwd(), '.sce');
|
|
73
|
+
if (!await fs.pathExists(sceDir)) {
|
|
74
74
|
throw new Error('CORE_PRINCIPLES violation: .sce directory not found. Project not adopted by sce.');
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
// Check for version.json
|
|
78
|
-
const versionFile = path.join(
|
|
78
|
+
const versionFile = path.join(sceDir, 'version.json');
|
|
79
79
|
if (!await fs.pathExists(versionFile)) {
|
|
80
80
|
console.warn('Warning: version.json not found. Consider running sce adoption.');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// Check for specs directory
|
|
84
|
-
const specsDir = path.join(
|
|
84
|
+
const specsDir = path.join(sceDir, 'specs');
|
|
85
85
|
if (!await fs.pathExists(specsDir)) {
|
|
86
86
|
await fs.ensureDir(specsDir);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// Check for steering directory
|
|
90
|
-
const steeringDir = path.join(
|
|
90
|
+
const steeringDir = path.join(sceDir, 'steering');
|
|
91
91
|
if (!await fs.pathExists(steeringDir)) {
|
|
92
92
|
console.warn('Warning: steering directory not found.');
|
|
93
93
|
}
|
|
@@ -426,7 +426,19 @@ async function loadAutoHandoffReleaseGateReports(projectPath, dirCandidate = nul
|
|
|
426
426
|
warnings.push(`skip invalid release gate payload: ${reportFile}`);
|
|
427
427
|
continue;
|
|
428
428
|
}
|
|
429
|
-
|
|
429
|
+
try {
|
|
430
|
+
entries.push(buildAutoHandoffReleaseGateHistoryEntry(
|
|
431
|
+
payload,
|
|
432
|
+
{
|
|
433
|
+
projectPath,
|
|
434
|
+
file: reportFile,
|
|
435
|
+
tag: parseAutoHandoffReleaseGateTag(path.basename(reportFile))
|
|
436
|
+
},
|
|
437
|
+
dependencies
|
|
438
|
+
));
|
|
439
|
+
} catch (error) {
|
|
440
|
+
warnings.push(`skip invalid release gate report entry: ${reportFile} (${error.message})`);
|
|
441
|
+
}
|
|
430
442
|
}
|
|
431
443
|
|
|
432
444
|
return {
|
|
@@ -459,13 +471,21 @@ async function loadAutoHandoffReleaseGateHistorySeed(projectPath, fileCandidate
|
|
|
459
471
|
};
|
|
460
472
|
}
|
|
461
473
|
const list = Array.isArray(payload && payload.entries) ? payload.entries : [];
|
|
462
|
-
const
|
|
474
|
+
const warnings = [];
|
|
475
|
+
const entries = [];
|
|
476
|
+
list
|
|
463
477
|
.filter(item => item && typeof item === 'object' && !Array.isArray(item))
|
|
464
|
-
.
|
|
478
|
+
.forEach((item, index) => {
|
|
479
|
+
try {
|
|
480
|
+
entries.push(buildAutoHandoffReleaseGateHistoryEntry(item, { projectPath }, dependencies));
|
|
481
|
+
} catch (error) {
|
|
482
|
+
warnings.push(`skip invalid gate history entry #${index + 1}: ${error.message}`);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
465
485
|
return {
|
|
466
486
|
file: filePath,
|
|
467
487
|
entries,
|
|
468
|
-
warnings
|
|
488
|
+
warnings
|
|
469
489
|
};
|
|
470
490
|
}
|
|
471
491
|
|
|
@@ -13,6 +13,8 @@ async function runAutoHandoff(projectPath, options = {}, dependencies = {}) {
|
|
|
13
13
|
loadGovernanceReleaseGateSignals,
|
|
14
14
|
completeAutoHandoffRunPhase,
|
|
15
15
|
evaluateAutoHandoffOntologyGateReasons,
|
|
16
|
+
auditSpecDeliverySync,
|
|
17
|
+
evaluateAutoHandoffSpecDeliveryGateReasons,
|
|
16
18
|
evaluateAutoHandoffReleaseGatePreflightGateReasons,
|
|
17
19
|
failAutoHandoffRunPhase,
|
|
18
20
|
buildAutoHandoffMoquiBaselineSnapshot,
|
|
@@ -63,6 +65,7 @@ async function runAutoHandoff(projectPath, options = {}, dependencies = {}) {
|
|
|
63
65
|
observability_snapshot: null,
|
|
64
66
|
spec_status: null,
|
|
65
67
|
ontology_validation: null,
|
|
68
|
+
spec_delivery_sync: null,
|
|
66
69
|
moqui_baseline: null,
|
|
67
70
|
scene_package_batch: null,
|
|
68
71
|
moqui_capability_coverage: null,
|
|
@@ -92,6 +95,9 @@ async function runAutoHandoff(projectPath, options = {}, dependencies = {}) {
|
|
|
92
95
|
result.ontology_validation = evaluateHandoffOntologyValidation(
|
|
93
96
|
plan && plan.handoff ? plan.handoff.ontology_validation : null
|
|
94
97
|
);
|
|
98
|
+
result.spec_delivery_sync = await auditSpecDeliverySync(projectPath, {
|
|
99
|
+
requireManifest: false
|
|
100
|
+
});
|
|
95
101
|
result.template_diff = await buildAutoHandoffTemplateDiff(projectPath, { manifest: options.manifest });
|
|
96
102
|
result.release_gate_preflight = buildAutoHandoffReleaseGatePreflight(
|
|
97
103
|
await loadGovernanceReleaseGateSignals(projectPath)
|
|
@@ -110,6 +116,15 @@ async function runAutoHandoff(projectPath, options = {}, dependencies = {}) {
|
|
|
110
116
|
completeAutoHandoffRunPhase(precheckPhase, {
|
|
111
117
|
validation: plan.validation,
|
|
112
118
|
phase_count: Array.isArray(plan.phases) ? plan.phases.length : 0,
|
|
119
|
+
spec_delivery_sync: result.spec_delivery_sync
|
|
120
|
+
? {
|
|
121
|
+
manifest_count: result.spec_delivery_sync.summary
|
|
122
|
+
? result.spec_delivery_sync.summary.manifest_count
|
|
123
|
+
: 0,
|
|
124
|
+
passed: result.spec_delivery_sync.passed === true,
|
|
125
|
+
reason: result.spec_delivery_sync.reason || null
|
|
126
|
+
}
|
|
127
|
+
: null,
|
|
113
128
|
template_compatibility: result.template_diff.compatibility,
|
|
114
129
|
release_gate_preflight: {
|
|
115
130
|
available: result.release_gate_preflight.available,
|
|
@@ -139,6 +154,28 @@ async function runAutoHandoff(projectPath, options = {}, dependencies = {}) {
|
|
|
139
154
|
if (ontologyGateReasons.length > 0) {
|
|
140
155
|
throw new Error(`handoff ontology validation gate failed: ${ontologyGateReasons.join('; ')}`);
|
|
141
156
|
}
|
|
157
|
+
const specDeliveryGateReasons = evaluateAutoHandoffSpecDeliveryGateReasons(
|
|
158
|
+
result.policy,
|
|
159
|
+
result.spec_delivery_sync
|
|
160
|
+
);
|
|
161
|
+
if (specDeliveryGateReasons.length > 0) {
|
|
162
|
+
throw new Error(`handoff spec delivery sync gate failed: ${specDeliveryGateReasons.join('; ')}`);
|
|
163
|
+
}
|
|
164
|
+
if (
|
|
165
|
+
result.policy.require_spec_delivery_sync !== true &&
|
|
166
|
+
result.spec_delivery_sync &&
|
|
167
|
+
result.spec_delivery_sync.reason !== 'no-manifests' &&
|
|
168
|
+
result.spec_delivery_sync.passed !== true
|
|
169
|
+
) {
|
|
170
|
+
const advisoryReasons = Array.isArray(result.spec_delivery_sync.violations)
|
|
171
|
+
? result.spec_delivery_sync.violations
|
|
172
|
+
: [];
|
|
173
|
+
if (advisoryReasons.length > 0) {
|
|
174
|
+
result.warnings.push(
|
|
175
|
+
`spec delivery sync advisory: ${advisoryReasons.join('; ')}`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
142
179
|
const releaseGatePreflightReasons = evaluateAutoHandoffReleaseGatePreflightGateReasons(
|
|
143
180
|
result.policy,
|
|
144
181
|
result.release_gate_preflight
|