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.
Files changed (67) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/bin/scene-capability-engine.js +42 -2
  3. package/docs/command-reference.md +27 -0
  4. package/docs/developer-guide.md +1 -1
  5. package/docs/document-governance.md +22 -2
  6. package/docs/releases/README.md +6 -0
  7. package/docs/releases/v3.6.39.md +24 -0
  8. package/docs/releases/v3.6.40.md +19 -0
  9. package/docs/releases/v3.6.41.md +20 -0
  10. package/docs/releases/v3.6.42.md +19 -0
  11. package/docs/releases/v3.6.43.md +17 -0
  12. package/docs/releases/v3.6.44.md +17 -0
  13. package/docs/spec-collaboration-guide.md +1 -1
  14. package/docs/state-migration-reconciliation-runbook.md +76 -0
  15. package/docs/state-storage-tiering.md +104 -0
  16. package/docs/zh/releases/README.md +6 -0
  17. package/docs/zh/releases/v3.6.39.md +24 -0
  18. package/docs/zh/releases/v3.6.40.md +19 -0
  19. package/docs/zh/releases/v3.6.41.md +20 -0
  20. package/docs/zh/releases/v3.6.42.md +19 -0
  21. package/docs/zh/releases/v3.6.43.md +17 -0
  22. package/docs/zh/releases/v3.6.44.md +17 -0
  23. package/lib/adoption/adoption-logger.js +1 -1
  24. package/lib/adoption/adoption-strategy.js +29 -29
  25. package/lib/adoption/detection-engine.js +16 -13
  26. package/lib/adoption/smart-orchestrator.js +3 -3
  27. package/lib/adoption/strategy-selector.js +19 -15
  28. package/lib/adoption/template-sync.js +3 -3
  29. package/lib/auto/autonomous-engine.js +5 -5
  30. package/lib/auto/handoff-release-gate-history-loaders-service.js +24 -4
  31. package/lib/auto/handoff-run-service.js +37 -0
  32. package/lib/backup/backup-system.js +10 -10
  33. package/lib/collab/collab-manager.js +8 -5
  34. package/lib/collab/dependency-manager.js +1 -1
  35. package/lib/commands/adopt.js +2 -2
  36. package/lib/commands/auto.js +239 -97
  37. package/lib/commands/collab.js +10 -4
  38. package/lib/commands/docs.js +8 -2
  39. package/lib/commands/scene.js +78 -18
  40. package/lib/commands/status.js +3 -3
  41. package/lib/commands/studio.js +8 -0
  42. package/lib/commands/watch.js +10 -1
  43. package/lib/governance/config-manager.js +16 -0
  44. package/lib/governance/diagnostic-engine.js +2 -1
  45. package/lib/governance/validation-engine.js +3 -2
  46. package/lib/repo/config-manager.js +2 -2
  47. package/lib/runtime/session-store.js +8 -0
  48. package/lib/spec/bootstrap/context-collector.js +5 -4
  49. package/lib/spec-gate/rules/default-rules.js +8 -8
  50. package/lib/state/sce-state-store.js +265 -0
  51. package/lib/state/state-migration-manager.js +27 -2
  52. package/lib/state/state-storage-policy.js +179 -0
  53. package/lib/upgrade/migration-engine.js +5 -5
  54. package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +3 -3
  55. package/lib/utils/tool-detector.js +4 -4
  56. package/lib/utils/validation.js +6 -6
  57. package/lib/watch/action-executor.js +10 -1
  58. package/lib/watch/event-debouncer.js +3 -0
  59. package/lib/watch/file-watcher.js +51 -10
  60. package/lib/watch/watch-manager.js +10 -1
  61. package/lib/workspace/multi/workspace-context-resolver.js +3 -3
  62. package/lib/workspace/multi/workspace-registry.js +3 -3
  63. package/lib/workspace/multi/workspace-state-manager.js +3 -3
  64. package/lib/workspace/spec-delivery-audit.js +553 -0
  65. package/lib/workspace/takeover-baseline.js +11 -0
  66. package/package.json +5 -1
  67. 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
- hasKiroDir: state.hasKiroDir,
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
- getKiroPath(projectPath) {
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} kiroPath - Path to .sce/ directory
68
+ * @param {string} scePath - Path to .sce/ directory
69
69
  * @returns {Promise<void>}
70
70
  */
71
- async createDirectoryStructure(kiroPath) {
72
- await ensureDirectory(kiroPath);
73
- await ensureDirectory(path.join(kiroPath, 'specs'));
74
- await ensureDirectory(path.join(kiroPath, 'steering'));
75
- await ensureDirectory(path.join(kiroPath, 'tools'));
76
- await ensureDirectory(path.join(kiroPath, 'config'));
77
- await ensureDirectory(path.join(kiroPath, 'backups'));
78
- await ensureDirectory(path.join(kiroPath, 'hooks'));
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 kiroPath = this.getKiroPath(projectPath);
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(kiroPath, file);
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 kiroPath = this.getKiroPath(projectPath);
211
+ const scePath = this.getScePath(projectPath);
212
212
 
213
213
  // Check if .sce/ already exists
214
- const kiroExists = await pathExists(kiroPath);
215
- if (kiroExists) {
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(kiroPath);
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 kiroPath = this.getKiroPath(projectPath);
302
+ const scePath = this.getScePath(projectPath);
303
303
 
304
304
  // Check if .sce/ exists
305
- const kiroExists = await pathExists(kiroPath);
306
- if (!kiroExists) {
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(kiroPath, 'version.json');
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(kiroPath, 'specs');
332
- const steeringPath = path.join(kiroPath, 'steering');
333
- const toolsPath = path.join(kiroPath, 'tools');
334
- const configPath = path.join(kiroPath, 'config');
335
- const backupsPath = path.join(kiroPath, 'backups');
336
- const hooksPath = path.join(kiroPath, 'hooks');
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 kiroPath = this.getKiroPath(projectPath);
440
+ const scePath = this.getScePath(projectPath);
441
441
 
442
442
  // Check if .sce/ exists
443
- const kiroExists = await pathExists(kiroPath);
444
- if (!kiroExists) {
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 kiroPath = path.join(projectPath, this.sceDir);
35
- const hasKiroDir = await pathExists(kiroPath);
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 (hasKiroDir) {
44
+ if (hasSceDir) {
45
45
  // Check for version.json
46
- const versionPath = path.join(kiroPath, this.versionFile);
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(kiroPath, this.specsDir);
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(kiroPath, this.toolsDir);
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 = hasKiroDir ? await this.detectConflicts(projectPath) : [];
76
+ const conflicts = hasSceDir ? await this.detectConflicts(projectPath) : [];
77
77
 
78
78
  return {
79
- hasKiroDir,
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 (!result.hasKiroDir) {
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 kiroPath = path.join(projectPath, this.sceDir);
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(kiroPath, templateFile);
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: ${result.hasKiroDir ? 'Yes' : 'No'}`);
258
+ lines.push(` .sce/ Directory: ${hasSceDir ? 'Yes' : 'No'}`);
256
259
 
257
- if (result.hasKiroDir) {
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 kiroPath = path.join(projectPath, '.sce');
320
+ const scePath = path.join(projectPath, '.sce');
321
321
 
322
322
  for (const templateFile of templateFiles) {
323
- const filePath = path.join(kiroPath, templateFile);
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(kiroPath, 'steering/CURRENT_CONTEXT.md');
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.hasKiroDir = data.hasKiroDir !== undefined ? data.hasKiroDir : false;
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 kiroPath = path.join(projectPath, '.sce');
65
- const versionPath = path.join(kiroPath, 'version.json');
66
- const specsPath = path.join(kiroPath, 'specs');
67
- const steeringPath = path.join(kiroPath, 'steering');
68
- const toolsPath = path.join(kiroPath, 'tools');
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 hasKiroDir = await this.fs.pathExists(kiroPath);
76
- const hasVersionFile = hasKiroDir && await this.fs.pathExists(versionPath);
77
- const hasSpecs = hasKiroDir && await this.fs.pathExists(specsPath);
78
- const hasSteering = hasKiroDir && await this.fs.pathExists(steeringPath);
79
- const hasTools = hasKiroDir && await this.fs.pathExists(toolsPath);
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 (hasKiroDir) {
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(kiroPath, templateFile);
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
- hasKiroDir,
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.hasKiroDir) {
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 kiroPath = path.join(projectPath, '.sce');
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(kiroPath, templateFile);
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 kiroPath = path.join(projectPath, '.sce');
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 kiroDir = path.join(process.cwd(), '.sce');
73
- if (!await fs.pathExists(kiroDir)) {
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(kiroDir, 'version.json');
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(kiroDir, 'specs');
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(kiroDir, 'steering');
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
- entries.push(buildAutoHandoffReleaseGateHistoryEntry(payload, { projectPath, file: reportFile, tag: parseAutoHandoffReleaseGateTag(path.basename(reportFile)) }, dependencies));
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 entries = list
474
+ const warnings = [];
475
+ const entries = [];
476
+ list
463
477
  .filter(item => item && typeof item === 'object' && !Array.isArray(item))
464
- .map(item => buildAutoHandoffReleaseGateHistoryEntry(item, { projectPath }, dependencies));
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