sillyspec 3.22.9 → 3.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,6 +34,20 @@ function gitQuiet(cwd, args) {
34
34
  }
35
35
  }
36
36
 
37
+ /**
38
+ * 过滤掉 worktree 基础设施文件(非交付物),让 apply 只关心真正的变更产出:
39
+ * - meta.json:worktree 元数据,baseline commit 中被跟踪、working-tree 被 CLI 改写
40
+ * (provisioning→linked 等)。它必须保持 modified(其 baselineCommit 字段是 apply diff
41
+ * 的锚点,保证 baseline overlay 文件不被误判为变更)。若不排除,modified-tracked 的
42
+ * meta.json 会落入 changedFiles,触发「不在 design.md 清单」误判,导致每个 execute 的
43
+ * assess 恒 BLOCKED。
44
+ * - .sillyspec/:变更文档 / 运行时产物,不属于源码交付。
45
+ * 对 modified-tracked(git diff)与 untracked(ls-files --others)一视同仁。
46
+ */
47
+ export function filterDeliverableFiles(files) {
48
+ return files.filter(f => !f.startsWith('.sillyspec/') && f !== 'meta.json');
49
+ }
50
+
37
51
  /**
38
52
  * 获取文件在 git 中的 blob hash(基于某个 commit/tree)
39
53
  * @param {string} cwd - git 工作区路径
@@ -106,11 +120,12 @@ export function applyWorktree(changeName, { cwd, checkOnly = false } = {}) {
106
120
 
107
121
  // untracked 新文件(diffBase 中不存在的文件)
108
122
  const untrackedRaw = gitQuiet(worktreePath, `ls-files --others --exclude-standard`);
109
- const untrackedFiles = untrackedRaw
110
- ? untrackedRaw.split('\n').filter(Boolean).filter(f => !f.startsWith('.sillyspec/') && f !== 'meta.json')
111
- : [];
123
+ const untrackedFiles = untrackedRaw ? untrackedRaw.split('\n').filter(Boolean) : [];
112
124
 
113
- changedFiles = [...new Set([...statusFiles, ...untrackedFiles])];
125
+ // 排除 worktree 基础设施文件(meta.json / .sillyspec/,见 filterDeliverableFiles)。
126
+ // 对 modified-tracked(statusFiles)与 untracked 一视同仁——否则 modified 的 meta.json
127
+ // 会被误算入 changedFiles,触发 design.md 清单校验失败(assess 恒 BLOCKED)。
128
+ changedFiles = filterDeliverableFiles([...new Set([...statusFiles, ...untrackedFiles])]);
114
129
  } catch (e) {
115
130
  result.errors.push(`获取变更文件列表失败: ${e.message}`);
116
131
  return result;
package/src/worktree.js CHANGED
@@ -453,6 +453,16 @@ export class WorktreeManager {
453
453
  return { branch: existingMeta.branch, worktreePath: existingMeta.worktreePath, baseHash: existingMeta.baseHash, mode: existingMeta.mode }
454
454
  }
455
455
 
456
+ // 供给 deps(与 native worktree 路径一致)。in-place-fallback 模式漏写 depsStatus 会致
457
+ // enforceDepsGate(run.js)把 undefined 当 unknown 阻断 execute --done,死锁无法推进。
458
+ // 见 docs/sillyspec/execute-inplace-deps-gate.md(2026-07-08 发现)。
459
+ let deps = {}
460
+ try {
461
+ deps = provisionDeps(worktreePath, this.cwd, { specBase: join(this.cwd, '.sillyspec') }) || {}
462
+ } catch (e) {
463
+ deps = { depsStatus: 'failed', depsError: `provisionDeps crashed: ${e.message}` }
464
+ }
465
+
456
466
  // 硬规则:禁止 self-overlay(source 和 target 相同时 overlay 必然冲突)
457
467
  const resolvedSource = resolve(this.cwd)
458
468
  const resolvedTarget = resolve(worktreePath)
@@ -474,6 +484,7 @@ export class WorktreeManager {
474
484
  baselineFiles: [],
475
485
  baselineCommit: null,
476
486
  baselineHash: null,
487
+ ...deps,
477
488
  }
478
489
  if (!existsSync(this.worktreeBase)) mkdirSync(this.worktreeBase, { recursive: true })
479
490
  const metaDir = join(this.worktreeBase, name)
@@ -510,6 +521,7 @@ export class WorktreeManager {
510
521
  baselineFiles,
511
522
  baselineCommit,
512
523
  baselineHash,
524
+ ...deps,
513
525
  };
514
526
 
515
527
  // in-place 模式下 meta 写入 worktreeBase(避免污染主工作区)