sillyspec 3.27.5 → 3.27.6

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 (194) hide show
  1. package/.worktrees/8d2d0392/.claude/skills/sillyspec-archive/SKILL.md +87 -0
  2. package/.worktrees/8d2d0392/.claude/skills/sillyspec-auto/SKILL.md +93 -0
  3. package/.worktrees/8d2d0392/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
  4. package/.worktrees/8d2d0392/.claude/skills/sillyspec-commit/SKILL.md +67 -0
  5. package/.worktrees/8d2d0392/.claude/skills/sillyspec-continue/SKILL.md +47 -0
  6. package/.worktrees/8d2d0392/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
  7. package/.worktrees/8d2d0392/.claude/skills/sillyspec-execute/SKILL.md +214 -0
  8. package/.worktrees/8d2d0392/.claude/skills/sillyspec-explore/SKILL.md +102 -0
  9. package/.worktrees/8d2d0392/.claude/skills/sillyspec-export/SKILL.md +57 -0
  10. package/.worktrees/8d2d0392/.claude/skills/sillyspec-init/SKILL.md +177 -0
  11. package/.worktrees/8d2d0392/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
  12. package/.worktrees/8d2d0392/.claude/skills/sillyspec-plan/SKILL.md +156 -0
  13. package/.worktrees/8d2d0392/.claude/skills/sillyspec-propose/SKILL.md +87 -0
  14. package/.worktrees/8d2d0392/.claude/skills/sillyspec-quick/SKILL.md +116 -0
  15. package/.worktrees/8d2d0392/.claude/skills/sillyspec-resume/SKILL.md +70 -0
  16. package/.worktrees/8d2d0392/.claude/skills/sillyspec-scan/SKILL.md +79 -0
  17. package/.worktrees/8d2d0392/.claude/skills/sillyspec-state/SKILL.md +64 -0
  18. package/.worktrees/8d2d0392/.claude/skills/sillyspec-status/SKILL.md +54 -0
  19. package/.worktrees/8d2d0392/.claude/skills/sillyspec-verify/SKILL.md +87 -0
  20. package/.worktrees/8d2d0392/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
  21. package/.worktrees/8d2d0392/.claude/skills/verify-per-user/SKILL.md +6 -0
  22. package/.worktrees/8d2d0392/.gitattributes +2 -0
  23. package/.worktrees/8d2d0392/.husky/pre-push +20 -0
  24. package/.worktrees/8d2d0392/README.md +203 -0
  25. package/.worktrees/8d2d0392/SKILL.md +91 -0
  26. package/.worktrees/8d2d0392/architecture-analysis.md +448 -0
  27. package/.worktrees/8d2d0392/bin/sillyspec.js +2 -0
  28. package/.worktrees/8d2d0392/eval/README.md +86 -0
  29. package/.worktrees/8d2d0392/eval/eval.config.json +28 -0
  30. package/.worktrees/8d2d0392/eval/lib/adapters/cli.mjs +63 -0
  31. package/.worktrees/8d2d0392/eval/lib/adapters/dryrun.mjs +27 -0
  32. package/.worktrees/8d2d0392/eval/lib/config.mjs +82 -0
  33. package/.worktrees/8d2d0392/eval/lib/db.mjs +76 -0
  34. package/.worktrees/8d2d0392/eval/lib/report.mjs +120 -0
  35. package/.worktrees/8d2d0392/eval/lib/runner.mjs +157 -0
  36. package/.worktrees/8d2d0392/eval/lib/tasks.mjs +73 -0
  37. package/.worktrees/8d2d0392/eval/lib/util.mjs +107 -0
  38. package/.worktrees/8d2d0392/eval/lib/workdir.mjs +30 -0
  39. package/.worktrees/8d2d0392/eval/run.mjs +83 -0
  40. package/.worktrees/8d2d0392/eval/tasks/001-slugify/files/package.json +6 -0
  41. package/.worktrees/8d2d0392/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
  42. package/.worktrees/8d2d0392/eval/tasks/001-slugify/task.json +6 -0
  43. package/.worktrees/8d2d0392/eval/tasks/001-slugify/verify.mjs +28 -0
  44. package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/package.json +6 -0
  45. package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
  46. package/.worktrees/8d2d0392/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
  47. package/.worktrees/8d2d0392/eval/tasks/002-each-day/task.json +6 -0
  48. package/.worktrees/8d2d0392/eval/tasks/002-each-day/verify.mjs +31 -0
  49. package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/data/notes.txt +5 -0
  50. package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/package.json +6 -0
  51. package/.worktrees/8d2d0392/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
  52. package/.worktrees/8d2d0392/eval/tasks/003-word-report/task.json +6 -0
  53. package/.worktrees/8d2d0392/eval/tasks/003-word-report/verify.mjs +38 -0
  54. package/.worktrees/8d2d0392/logo.jpg +0 -0
  55. package/.worktrees/8d2d0392/package-lock.json +977 -0
  56. package/.worktrees/8d2d0392/package.json +41 -0
  57. package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
  58. package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
  59. package/.worktrees/8d2d0392/packages/dashboard/dist/favicon.jpg +0 -0
  60. package/.worktrees/8d2d0392/packages/dashboard/dist/index.html +16 -0
  61. package/.worktrees/8d2d0392/packages/dashboard/dist/logo.jpg +0 -0
  62. package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-dashboard.html +836 -0
  63. package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-overview.html +256 -0
  64. package/.worktrees/8d2d0392/packages/dashboard/package.json +25 -0
  65. package/.worktrees/8d2d0392/packages/dashboard/server/executor.js +95 -0
  66. package/.worktrees/8d2d0392/packages/dashboard/server/index.js +679 -0
  67. package/.worktrees/8d2d0392/packages/dashboard/server/parser.js +526 -0
  68. package/.worktrees/8d2d0392/packages/dashboard/server/watcher.js +357 -0
  69. package/.worktrees/8d2d0392/src/agent-session-log.js +803 -0
  70. package/.worktrees/8d2d0392/src/change-list.js +290 -0
  71. package/.worktrees/8d2d0392/src/change-risk-profile.js +339 -0
  72. package/.worktrees/8d2d0392/src/check-primitives.js +65 -0
  73. package/.worktrees/8d2d0392/src/classify-change.js +109 -0
  74. package/.worktrees/8d2d0392/src/commit-suggest.js +202 -0
  75. package/.worktrees/8d2d0392/src/config-cat.js +88 -0
  76. package/.worktrees/8d2d0392/src/config-schema.js +328 -0
  77. package/.worktrees/8d2d0392/src/constants.js +97 -0
  78. package/.worktrees/8d2d0392/src/contract-matrix.js +533 -0
  79. package/.worktrees/8d2d0392/src/datetime.js +20 -0
  80. package/.worktrees/8d2d0392/src/db-engine.js +55 -0
  81. package/.worktrees/8d2d0392/src/db.js +381 -0
  82. package/.worktrees/8d2d0392/src/decision-distill.js +517 -0
  83. package/.worktrees/8d2d0392/src/dispatch/backends/local-agent.js +155 -0
  84. package/.worktrees/8d2d0392/src/dispatch/backends/sillyhub-mcp.js +245 -0
  85. package/.worktrees/8d2d0392/src/dispatch/probe.js +246 -0
  86. package/.worktrees/8d2d0392/src/dispatch/strategy.js +121 -0
  87. package/.worktrees/8d2d0392/src/docs-check.js +825 -0
  88. package/.worktrees/8d2d0392/src/docs-debt.js +393 -0
  89. package/.worktrees/8d2d0392/src/docs-gate.js +107 -0
  90. package/.worktrees/8d2d0392/src/doctor-diagnostics.js +1099 -0
  91. package/.worktrees/8d2d0392/src/endpoint-extractor.js +409 -0
  92. package/.worktrees/8d2d0392/src/foreign-declared.js +78 -0
  93. package/.worktrees/8d2d0392/src/fs-atomic.js +73 -0
  94. package/.worktrees/8d2d0392/src/git-helper.js +150 -0
  95. package/.worktrees/8d2d0392/src/hooks/claude-pre-tool-use.cjs +130 -0
  96. package/.worktrees/8d2d0392/src/hooks/worktree-guard.js +892 -0
  97. package/.worktrees/8d2d0392/src/index.js +2854 -0
  98. package/.worktrees/8d2d0392/src/init.js +626 -0
  99. package/.worktrees/8d2d0392/src/knowledge-match.js +231 -0
  100. package/.worktrees/8d2d0392/src/local-detect.js +128 -0
  101. package/.worktrees/8d2d0392/src/local-register.js +104 -0
  102. package/.worktrees/8d2d0392/src/machine-interface.js +543 -0
  103. package/.worktrees/8d2d0392/src/migrate.js +127 -0
  104. package/.worktrees/8d2d0392/src/module-impact.js +166 -0
  105. package/.worktrees/8d2d0392/src/module-resolve.js +222 -0
  106. package/.worktrees/8d2d0392/src/modules.js +580 -0
  107. package/.worktrees/8d2d0392/src/plan-adopt-waves.js +126 -0
  108. package/.worktrees/8d2d0392/src/progress/change-registry.js +336 -0
  109. package/.worktrees/8d2d0392/src/progress/consistency-doctor.js +355 -0
  110. package/.worktrees/8d2d0392/src/progress/shared.js +36 -0
  111. package/.worktrees/8d2d0392/src/progress/stage-machine.js +594 -0
  112. package/.worktrees/8d2d0392/src/progress/step-store.js +265 -0
  113. package/.worktrees/8d2d0392/src/progress.js +1304 -0
  114. package/.worktrees/8d2d0392/src/quick-recommend.js +115 -0
  115. package/.worktrees/8d2d0392/src/quicklog.js +883 -0
  116. package/.worktrees/8d2d0392/src/review-tier.js +77 -0
  117. package/.worktrees/8d2d0392/src/run/command.js +1641 -0
  118. package/.worktrees/8d2d0392/src/run/complete-handlers.js +1552 -0
  119. package/.worktrees/8d2d0392/src/run/complete.js +1259 -0
  120. package/.worktrees/8d2d0392/src/run/concurrent-detect.js +156 -0
  121. package/.worktrees/8d2d0392/src/run/gates.js +1094 -0
  122. package/.worktrees/8d2d0392/src/run/multi-repo-context.js +208 -0
  123. package/.worktrees/8d2d0392/src/run/next.js +128 -0
  124. package/.worktrees/8d2d0392/src/run/prompt.js +843 -0
  125. package/.worktrees/8d2d0392/src/run/quick-audit.js +186 -0
  126. package/.worktrees/8d2d0392/src/run/scan-profile.js +269 -0
  127. package/.worktrees/8d2d0392/src/run/shared.js +1562 -0
  128. package/.worktrees/8d2d0392/src/run/stage.js +587 -0
  129. package/.worktrees/8d2d0392/src/run.js +23 -0
  130. package/.worktrees/8d2d0392/src/scan-diff.js +388 -0
  131. package/.worktrees/8d2d0392/src/scan-postcheck.js +524 -0
  132. package/.worktrees/8d2d0392/src/scan-staleness.js +95 -0
  133. package/.worktrees/8d2d0392/src/setup.js +414 -0
  134. package/.worktrees/8d2d0392/src/sillyhub-mcp/client.js +597 -0
  135. package/.worktrees/8d2d0392/src/sillyhub-mcp/config.js +62 -0
  136. package/.worktrees/8d2d0392/src/spec-dir-typo.js +50 -0
  137. package/.worktrees/8d2d0392/src/spec-sync.js +358 -0
  138. package/.worktrees/8d2d0392/src/stage-contract-engine.js +171 -0
  139. package/.worktrees/8d2d0392/src/stage-contract-spec.js +551 -0
  140. package/.worktrees/8d2d0392/src/stage-contract.js +948 -0
  141. package/.worktrees/8d2d0392/src/stage-review.js +597 -0
  142. package/.worktrees/8d2d0392/src/stages/archive.js +199 -0
  143. package/.worktrees/8d2d0392/src/stages/brainstorm-auto.js +285 -0
  144. package/.worktrees/8d2d0392/src/stages/brainstorm.js +624 -0
  145. package/.worktrees/8d2d0392/src/stages/cmd-existence.js +145 -0
  146. package/.worktrees/8d2d0392/src/stages/doctor.js +540 -0
  147. package/.worktrees/8d2d0392/src/stages/execute.js +1277 -0
  148. package/.worktrees/8d2d0392/src/stages/explore.js +43 -0
  149. package/.worktrees/8d2d0392/src/stages/index.js +29 -0
  150. package/.worktrees/8d2d0392/src/stages/knowledge.js +514 -0
  151. package/.worktrees/8d2d0392/src/stages/plan-postcheck.js +1398 -0
  152. package/.worktrees/8d2d0392/src/stages/plan.js +651 -0
  153. package/.worktrees/8d2d0392/src/stages/quick.js +167 -0
  154. package/.worktrees/8d2d0392/src/stages/scan.js +608 -0
  155. package/.worktrees/8d2d0392/src/stages/status.js +64 -0
  156. package/.worktrees/8d2d0392/src/stages/verify.js +246 -0
  157. package/.worktrees/8d2d0392/src/sync.js +1470 -0
  158. package/.worktrees/8d2d0392/src/task-review.js +1498 -0
  159. package/.worktrees/8d2d0392/src/taskcard-placeholders.js +21 -0
  160. package/.worktrees/8d2d0392/src/taskcard.js +217 -0
  161. package/.worktrees/8d2d0392/src/verify-postcheck.js +1718 -0
  162. package/.worktrees/8d2d0392/src/verify-probes.js +327 -0
  163. package/.worktrees/8d2d0392/src/version.js +19 -0
  164. package/.worktrees/8d2d0392/src/workflow.js +600 -0
  165. package/.worktrees/8d2d0392/src/workspace.js +125 -0
  166. package/.worktrees/8d2d0392/src/worktree-apply.js +1611 -0
  167. package/.worktrees/8d2d0392/src/worktree-deps.js +576 -0
  168. package/.worktrees/8d2d0392/src/worktree.js +1838 -0
  169. package/.worktrees/8d2d0392/templates/claude-instruction.md +23 -0
  170. package/.worktrees/8d2d0392/templates/prompts/taskcard-rules.md +29 -0
  171. package/.worktrees/8d2d0392/templates/prompts/testcase-design.md +12 -0
  172. package/.worktrees/8d2d0392/templates/prompts/verify-probes.md +32 -0
  173. package/.worktrees/8d2d0392/templates/workflows/archive-impact.yaml +79 -0
  174. package/.worktrees/8d2d0392/templates/workflows/scan-docs.yaml +132 -0
  175. package/architecture-analysis.md +448 -0
  176. package/package.json +1 -1
  177. package/src/contract-matrix.js +1 -1
  178. package/src/endpoint-extractor.js +41 -52
  179. package/src/foreign-declared.js +78 -1
  180. package/src/index.js +9 -7
  181. package/src/run/command.js +2 -1
  182. package/src/run/gates.js +17 -0
  183. package/src/run/stage.js +8 -5
  184. package/src/stage-contract-spec.js +1 -1
  185. package/src/stages/brainstorm-auto.js +43 -1
  186. package/src/stages/brainstorm.js +2 -0
  187. package/src/stages/plan.js +8 -4
  188. package/src/stages/verify.js +1 -1
  189. package/src/taskcard.js +33 -0
  190. package/src/verify-probes.js +27 -6
  191. package/src/worktree-apply.js +451 -286
  192. package/src/worktree-deps.js +93 -1
  193. package/src/worktree.js +84 -19
  194. package/templates/prompts/taskcard-rules.md +1 -1
@@ -8,8 +8,9 @@
8
8
  * 见 change 2026-06-28-worktree-deps-provision / D-005@v1, D-007@v1。
9
9
  */
10
10
 
11
- import { existsSync, readFileSync, realpathSync, lstatSync } from 'fs';
11
+ import { existsSync, readFileSync, realpathSync, lstatSync, readdirSync } from 'fs';
12
12
  import { join, isAbsolute, relative, resolve as resolvePath, sep as pathSep } from 'path';
13
+ import { fileURLToPath } from 'url';
13
14
  import { execFileSync } from 'child_process';
14
15
  import { createHash } from 'crypto';
15
16
 
@@ -574,3 +575,94 @@ export function checkDepsFreshness(meta, wtPath, mainCwd) {
574
575
  wtHash, mainHash, metaLockHash,
575
576
  };
576
577
  }
578
+
579
+ /**
580
+ * editable install 越界探测(坑 worktree-editable-install-escape,2026-08-25 用户实证
581
+ * gen:types 坑):worktree 内 Python venv 的 editable install 指向 worktree 外(典型:
582
+ * 主仓 checkout 路径)时,gen:types / 后端命令 / pytest 静默加载 worktree 外的旧代码——
583
+ * 改动不生效且无任何报错,此前靠模块文档注意事项人工记忆,本函数把它 CLI 检查化
584
+ * (worktree doctor 调用)。
585
+ *
586
+ * 覆盖三种 editable 痕迹:
587
+ * 1. 路径型 .pth(setuptools 旧式):.pth 内容行即绝对路径
588
+ * 2. PEP 660 __editable__.<pkg>-*.pth + __editable___<pkg>_*_finder.py(MAPPING 表绝对路径)
589
+ * 3. *.dist-info/direct_url.json { dir_info.editable: true, url: file:///... }
590
+ * 判定统一为「目标路径 resolve 后不在 worktree 内」;venv/文件读取失败按无越界处理
591
+ * (doctor 体检语义,不阻断)。
592
+ *
593
+ * @param {string} wtPath worktree 根目录
594
+ * @returns {Array<{ pkg: string, target: string, via: string }>} 越界清单(空 = 干净或无 venv)
595
+ */
596
+ export function detectEditableInstallEscape(wtPath) {
597
+ const offenders = [];
598
+ if (!wtPath || !existsSync(wtPath)) return offenders;
599
+ const wtRoot = resolvePath(wtPath);
600
+ const isEscape = (target) => {
601
+ if (!target || !isAbsolute(target)) return false;
602
+ const rel = relative(wtRoot, resolvePath(target));
603
+ return rel.startsWith('..') || isAbsolute(rel);
604
+ };
605
+ const seen = new Set();
606
+ const push = (pkg, target, via) => {
607
+ const key = `${pkg}|${target}`;
608
+ if (seen.has(key)) return;
609
+ seen.add(key);
610
+ offenders.push({ pkg, target, via });
611
+ };
612
+
613
+ for (const venvName of ['.venv', 'venv']) {
614
+ const venvDir = join(wtPath, venvName);
615
+ if (!existsSync(venvDir)) continue;
616
+ // site-packages 候选:Windows Lib/site-packages + POSIX lib/python*/site-packages
617
+ const spCandidates = [join(venvDir, 'Lib', 'site-packages')];
618
+ try {
619
+ for (const e of readdirSync(join(venvDir, 'lib'), { withFileTypes: true })) {
620
+ if (e.isDirectory() && e.name.toLowerCase().startsWith('python')) {
621
+ spCandidates.push(join(venvDir, 'lib', e.name, 'site-packages'));
622
+ }
623
+ }
624
+ } catch { /* 无 lib 目录(Windows 布局)跳过 */ }
625
+ for (const sp of spCandidates) {
626
+ if (!existsSync(sp)) continue;
627
+ let entries = [];
628
+ try { entries = readdirSync(sp, { withFileTypes: true }); } catch { continue; }
629
+ for (const e of entries) {
630
+ if (!e.isFile()) continue;
631
+ const fp = join(sp, e.name);
632
+ if (e.name.endsWith('.pth')) {
633
+ let lines = [];
634
+ try { lines = readFileSync(fp, 'utf8').split('\n'); } catch { continue; }
635
+ for (const raw of lines) {
636
+ const line = raw.trim();
637
+ if (!line || line.startsWith('#') || line.startsWith('import ')) continue;
638
+ if (isEscape(line)) push(e.name.replace(/\.pth$/, ''), line, '.pth');
639
+ }
640
+ } else if (/^__editable___.+_finder\.py$/.test(e.name)) {
641
+ let src = '';
642
+ try { src = readFileSync(fp, 'utf8'); } catch { continue; }
643
+ const mm = /MAPPING\s*=\s*\{([\s\S]*?)\}/.exec(src);
644
+ if (!mm) continue;
645
+ const pkg = e.name.replace(/^__editable___/, '').replace(/_finder\.py$/, '');
646
+ for (const pm of mm[1].matchAll(/'((?:[^'\\]|\\.)*)'/g)) {
647
+ const target = pm[1].replace(/\\\\/g, '\\');
648
+ if (isEscape(target)) push(pkg, target, 'pep660-finder');
649
+ }
650
+ }
651
+ }
652
+ // direct_url.json(pip/uv 现代 editable 标记)
653
+ for (const e of entries) {
654
+ if (!e.isDirectory() || !e.name.endsWith('.dist-info')) continue;
655
+ const du = join(sp, e.name, 'direct_url.json');
656
+ if (!existsSync(du)) continue;
657
+ try {
658
+ const meta = JSON.parse(readFileSync(du, 'utf8'));
659
+ if (meta?.dir_info?.editable && typeof meta.url === 'string' && meta.url.startsWith('file://')) {
660
+ const target = fileURLToPath(meta.url);
661
+ if (isEscape(target)) push(e.name.replace(/\.dist-info$/, ''), target, 'direct_url');
662
+ }
663
+ } catch { /* 解析失败跳过 */ }
664
+ }
665
+ }
666
+ }
667
+ return offenders;
668
+ }
package/src/worktree.js CHANGED
@@ -12,7 +12,7 @@ import { execFileSync } from 'child_process';
12
12
  import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, readlinkSync, unlinkSync, copyFileSync } from 'fs';
13
13
  import { join, resolve, dirname, relative, isAbsolute } from 'path';
14
14
  import { createHash } from 'crypto';
15
- import { provisionDeps, checkDepsFreshness } from './worktree-deps.js';
15
+ import { provisionDeps, checkDepsFreshness, detectEditableInstallEscape } from './worktree-deps.js';
16
16
  import { writeAtomicSync } from './fs-atomic.js';
17
17
  import { git, gitQuiet } from './git-helper.js';
18
18
 
@@ -416,7 +416,7 @@ export class WorktreeManager {
416
416
  * @returns {{ branch: string, worktreePath: string, baseHash: string }}
417
417
  * @throws {Error} worktree 已存在、git 不可用、changeName 为空
418
418
  */
419
- create(changeName, { base } = {}) {
419
+ create(changeName, { base, adoptBranch = false } = {}) {
420
420
  const name = validateChangeName(changeName);
421
421
  const worktreePath = this.getWorktreePath(name);
422
422
  const branch = BRANCH_PREFIX + name;
@@ -481,18 +481,29 @@ export class WorktreeManager {
481
481
  try { safeRemoveWorktreeDir(worktreePath); } catch (unlinkErr) {
482
482
  throw new Error(`幽灵 worktree 安全清理失败(已阻断,主仓 node_modules 未受影响):${unlinkErr.message}`)
483
483
  }
484
- // 同步清理 git worktree 注册 + 残留分支,否则目录虽删但 git 内部状态未清,
485
- // 后续 git worktree add 会因「worktree 已注册」或「分支已存在」失败
484
+ // 同步清理 git worktree 注册(git 内部状态),否则后续 worktree add 会因「已注册」失败。
485
+ // 不再盲删同名分支(坑 worktree-user-branch-conflict,2026-08-24 用户反馈五期①:该分支
486
+ // 可能是用户有意创建的,无凭据区分遗留 vs 自建——统一交由下方分支决策菜单处置)。
486
487
  try { gitQuiet(this.cwd, ['worktree', 'prune'], { timeout: 30000 }); } catch {}
487
- try { gitQuiet(this.cwd, ['branch', '-D', branch]); } catch {}
488
488
  } else {
489
489
  throw new Error(`worktree already exists: ${name}. Run cleanup first.`);
490
490
  }
491
491
  }
492
492
 
493
- // 2. 检查分支是否已存在
494
- if (gitQuiet(this.cwd, ['rev-parse', '--verify', `refs/heads/${branch}`])) {
495
- throw new Error(`branch already exists: ${branch}. Run cleanup first.`);
493
+ // 2. 检查分支是否已存在(坑 worktree-user-branch-conflict,2026-08-24 用户反馈五期①:
494
+ // 「用户要求在指定分支上做」时既有同名分支让 execute 直接死锁,旧报错只说 Run cleanup
495
+ // first、修复建议无条件推荐删分支——用户自建分支被引导误删。现给决策菜单;--adopt-branch
496
+ // 显式收编:检出该分支为 worktree 工作分支,分支现状作 baseline,仅后续新改动计入交付。)
497
+ const branchExists = !!gitQuiet(this.cwd, ['rev-parse', '--verify', `refs/heads/${branch}`]);
498
+ let adoptedBranch = false;
499
+ if (branchExists && !adoptBranch) {
500
+ throw new Error(
501
+ `分支已存在:${branch}(worktree create 需新建同名分支,直接冲突)。按实际情况三选一:\n` +
502
+ ` ① 本变更的遗留分支(上次 execute 残留、内容已落地或作废):确认后 git branch -D ${branch} 再重跑;\n` +
503
+ ` ② 该分支是你有意让本变更在其上做的(用户指定分支):sillyspec run execute --change ${name} --adopt-branch\n` +
504
+ ` —— 收编为 worktree 工作分支,分支既有内容计入 baseline(仅后续新改动算交付 diff);\n` +
505
+ ` ③ 换变更名重跑。`
506
+ );
496
507
  }
497
508
 
498
509
  // 3. 解析 base 分支
@@ -521,7 +532,17 @@ export class WorktreeManager {
521
532
 
522
533
  // 5. 创建 worktree(含版本检测 + sandbox fallback)
523
534
  try {
524
- git(this.cwd, ['worktree', 'add', worktreePath, '-b', branch, baseHash], { timeout: 120000 });
535
+ if (branchExists && adoptBranch) {
536
+ // --adopt-branch 收编(坑 worktree-user-branch-conflict):检出既有分支为工作分支(不 -b),
537
+ // 分支当前 HEAD 作 baseHash——分支存量内容归 baseline,仅子代理新改动计入交付 diff
538
+ // (与 dirty baseline overlay 同哲学)。baseBranch 仍记主仓侧锚(sync 诊断用)。
539
+ git(this.cwd, ['worktree', 'add', worktreePath, branch], { timeout: 120000 });
540
+ baseHash = git(this.cwd, ['rev-parse', branch]);
541
+ adoptedBranch = true;
542
+ console.log(`📌 已收编既有分支 ${branch} 为 worktree 工作分支(--adopt-branch):baseline = 分支 HEAD ${String(baseHash).slice(0, 8)},分支既有内容不计交付 diff`);
543
+ } else {
544
+ git(this.cwd, ['worktree', 'add', worktreePath, '-b', branch, baseHash], { timeout: 120000 });
545
+ }
525
546
  } catch (e) {
526
547
  const check = isGitWorktreeSupported(this.cwd);
527
548
  if (!check.supported) {
@@ -582,8 +603,9 @@ export class WorktreeManager {
582
603
 
583
604
  // 5.7 创建 baseline checkpoint(有 dirty baseline 时才创建)
584
605
  let baselineCommit = null;
606
+ if (adoptedBranch) baselineCommit = baseHash; // adopt:分支 HEAD 恒作交付 diff 锚(无 dirty overlay 也要锚定)
585
607
  if (baselineFiles.length > 0) {
586
- baselineCommit = this._createBaselineCheckpoint(worktreePath, name);
608
+ baselineCommit = this._createBaselineCheckpoint(worktreePath, name, baselineFiles);
587
609
  }
588
610
 
589
611
  // 5.8 依赖供给(change 2026-06-28-worktree-deps-provision)
@@ -616,12 +638,13 @@ export class WorktreeManager {
616
638
  depsLockHash: deps.depsLockHash || null,
617
639
  depsCheckedAt: deps.depsCheckedAt || null,
618
640
  ...(deps.depsError ? { depsError: deps.depsError } : {}),
641
+ ...(adoptedBranch ? { adoptedBranch: true } : {}),
619
642
  };
620
643
 
621
644
  const metaPath = join(worktreePath, META_FILE);
622
645
  writeMetaAtomic(metaPath, meta);
623
646
 
624
- return { branch, worktreePath, baseHash, mode: meta.mode, syncDiagnostic };
647
+ return { branch, worktreePath, baseHash, mode: meta.mode, syncDiagnostic, ...(adoptedBranch ? { adoptedBranch: true } : {}) };
625
648
  }
626
649
 
627
650
  /**
@@ -719,7 +742,7 @@ export class WorktreeManager {
719
742
 
720
743
  let baselineCommit = null;
721
744
  if (baselineFiles.length > 0) {
722
- baselineCommit = this._createBaselineCheckpoint(this.cwd, name);
745
+ baselineCommit = this._createBaselineCheckpoint(this.cwd, name, baselineFiles);
723
746
  }
724
747
 
725
748
  const meta = {
@@ -1017,6 +1040,11 @@ export class WorktreeManager {
1017
1040
 
1018
1041
  // 4. 删除分支(fail-closed:task review 引用可达性校验。force 也不绕过——force 语义 =
1019
1042
  // 丢弃内容,不含丢弃审计链;确要删用 git branch -D 手动执行)
1043
+ // native-worktree 豁免(坑 worktree-user-branch-conflict,2026-08-24):该模式 meta.branch
1044
+ // 记的是用户自己的检出分支(非 sillyspec 所建),force 也不删——只清 sillyspec 侧注册。
1045
+ if (mode === 'native-worktree') {
1046
+ details.push(`branch kept: native-worktree 模式的 branch(${branch})是用户自己的检出分支,不删除`);
1047
+ } else
1020
1048
  // 升级(坑 cleanup-branch-review-anchor-tag,2026-08-21 实证):分支被 review.json 引用
1021
1049
  // 时不再只能手动保留——打 `sillyspec-audit/<branch>` 轻量 tag 锚定分支 tip(ref 前缀独立
1022
1050
  // 于 sillyspec/* 分支族,doctor 孤儿分支扫描不误伤),commit 经 tag 保持可达(gc 安全),
@@ -1176,16 +1204,19 @@ export class WorktreeManager {
1176
1204
  // 数据源不一致——并行会话的活跃变更(meta 已清/in-place/平台模式 meta 在别处)分支会被全局
1177
1205
  // doctor --fix 误删。删分支前交叉核对:分支名 ∈ 活跃变更 → 保留(fixable:false 提示人工确认)。
1178
1206
  // 先探 DB 文件存在再实例化 ProgressManager(坑7 读路径建库:_ensureDB 不存在会建库污染)。
1179
- // null = 进度库存在但读失败(无法判定活跃态,保守不自动删);空 Set = 无库/无活跃变更
1180
- // (git-only 工作流零回归,孤儿分支照删)。
1207
+ // null = 无法核对(读失败 或 无进度库——坑 worktree-user-branch-conflict 2026-08-24 收紧:
1208
+ // 无库时无法区分孤儿与用户自建分支,从「照删」改保守保留,git-only 便利让位于不误删);
1209
+ // Set = 进度库可读(空集=无活跃变更,非活跃孤儿仍可删)。
1181
1210
  let activeChanges = null;
1211
+ let dbMissing = false;
1182
1212
  try {
1183
1213
  const specBaseOfWt = dirname(dirname(this.worktreeBase)); // <specBase>(.runtime/worktrees 上两级)
1184
1214
  if (existsSync(join(specBaseOfWt, '.runtime', 'sillyspec.db'))) {
1185
1215
  const { ProgressManager } = await import('./progress.js');
1186
1216
  activeChanges = new Set(new ProgressManager({ specDir: specBaseOfWt }).listChanges(this.cwd));
1187
1217
  } else {
1188
- activeChanges = new Set();
1218
+ activeChanges = null;
1219
+ dbMissing = true;
1189
1220
  }
1190
1221
  } catch (e) {
1191
1222
  console.warn(`⚠️ doctor 进度库读取失败,孤儿分支的活跃态交叉核对不可用(保守不自动删): ${e.message}`);
@@ -1296,6 +1327,17 @@ export class WorktreeManager {
1296
1327
  (r.ok ? fixed : unfixable).push(r.msg);
1297
1328
  }
1298
1329
  }
1330
+ // editable install 越界检查(坑 worktree-editable-install-escape,2026-08-25 实证
1331
+ // gen:types 坑):worktree venv 的 editable install 指向 worktree 外(主仓)时,
1332
+ // gen:types/后端命令/pytest 静默加载旧代码零报错——此前靠模块文档人工记忆,内建检查化。
1333
+ // fixable:false(重装决策留给用户:uv sync / pip install -e . 需按项目方式选)。
1334
+ try {
1335
+ const escapes = detectEditableInstallEscape(wtPath);
1336
+ if (escapes.length > 0) {
1337
+ issues.push({ type: 'editable-install-escape', name, fixable: false,
1338
+ detail: `worktree venv 含 ${escapes.length} 个 editable install 指向 worktree 外(${escapes.slice(0, 5).map(e => `${e.pkg}→${e.target}`).join(', ')}${escapes.length > 5 ? ' 等' : ''})——gen:types/后端命令会静默加载 worktree 外旧代码。修复:在 worktree 内重装(uv sync / uv pip install -e .)后重跑生成命令` });
1339
+ }
1340
+ } catch { /* 探测失败不阻断 doctor */ }
1299
1341
  }
1300
1342
  }
1301
1343
 
@@ -1375,9 +1417,20 @@ export class WorktreeManager {
1375
1417
  continue;
1376
1418
  }
1377
1419
  if (activeChanges === null) {
1378
- // 进度库读失败:无法判定活跃态,保守不自动删(宁可漏删不可误删并行会话分支)
1420
+ // 无法核对活跃态(读失败 或 无进度库):保守不自动删(宁可漏删不可误删——
1421
+ // 坑 worktree-user-branch-conflict:无库时该分支可能是用户自建的,无凭据区分)
1422
+ const why = dbMissing ? '无进度库,无法区分孤儿与用户自建分支' : '进度库不可读,无法交叉核对活跃态'
1423
+ issues.push({ type: 'orphan-branch', name, fixable: false,
1424
+ detail: `分支疑似残留(无对应 meta,且${why}): ${branch}——保守不自动删,人工确认后 git branch -D ${branch}` });
1425
+ continue;
1426
+ }
1427
+ // review 锚点复核(坑 worktree-user-branch-conflict):分支上有 task review base/head
1428
+ // 引用的 commit → 审计链在用,不自动删(与 cleanup 的分支删除审计保护同口径)
1429
+ let reviewRefs = [];
1430
+ try { reviewRefs = this._branchReviewReferences(branch); } catch { reviewRefs = [] }
1431
+ if (reviewRefs.length > 0) {
1379
1432
  issues.push({ type: 'orphan-branch', name, fixable: false,
1380
- detail: `分支疑似残留(无对应 meta,且进度库不可读无法交叉核对活跃态): ${branch}——保守不自动删,人工确认后 git branch -D ${branch}` });
1433
+ detail: `分支无对应 meta 但被 ${reviewRefs.length} 个 task review.json 的 base/head 引用(审计锚点)——保留。确要删除:先归档/迁移审计引用,或手动 git branch -D ${branch}` });
1381
1434
  continue;
1382
1435
  }
1383
1436
  issues.push({ type: 'orphan-branch', name, detail: `分支残留(无对应 meta,且非活跃变更): ${branch}`, fixable: true });
@@ -1748,9 +1801,13 @@ export class WorktreeManager {
1748
1801
  * 用于区分 "前置 dirty baseline" 和 "子代理新增改动"
1749
1802
  * @param {string} worktreePath
1750
1803
  * @param {string} changeName
1804
+ * @param {string[]} [baselineFiles] 主仓并行在途文件清单(_overlayBaseline 返回的 files,
1805
+ * 已排除 .sillyspec)——写入提交信息正文(坑 baseline-checkpoint-opaque-carriage,
1806
+ * 2026-08-24 用户反馈三期③:checkpoint 把主仓并行脏文件带进分支 diff,逐任务归因要靠
1807
+ * 人肉区分;清单入 message 后 `git log` 一眼可辨、可机械排除)
1751
1808
  * @returns {string} commit hash
1752
1809
  */
1753
- _createBaselineCheckpoint(worktreePath, changeName) {
1810
+ _createBaselineCheckpoint(worktreePath, changeName, baselineFiles = []) {
1754
1811
  // 使用临时 git identity,避免用户未配置 user.name/user.email 导致失败
1755
1812
  const env = {
1756
1813
  GIT_AUTHOR_NAME: 'sillyspec',
@@ -1765,13 +1822,21 @@ export class WorktreeManager {
1765
1822
  if (!status) {
1766
1823
  return gitQuiet(worktreePath, ['rev-parse', 'HEAD']);
1767
1824
  }
1825
+ // 提交信息正文列夹带文件(封顶 30 行,超出记总数)——清单与 meta.baselineFiles 同源
1826
+ const CAP = 30
1827
+ const files = Array.isArray(baselineFiles) ? baselineFiles.filter(Boolean) : []
1828
+ const body = files.length > 0
1829
+ ? ['\n主仓并行在途文件(非本变更改动,逐任务归因时排除):',
1830
+ ...files.slice(0, CAP).map(f => `- ${f}`),
1831
+ ...(files.length > CAP ? [`…等共 ${files.length} 个`] : [])].join('\n')
1832
+ : ''
1768
1833
  // --no-verify:baseline 是锚点不是交付物,只是把主仓库 dirty 文件快照到 worktree
1769
1834
  // 分支上以便区分「前置 baseline」与「子代理新增改动」。它不该触发项目 pre-commit
1770
1835
  // hook(如 ruff format),否则主仓库 dirty 文件中任一不达标的会被 hook reformat
1771
1836
  // 致 commit 失败 → worktree 创建失败 → execute 无法启动。
1772
1837
  execFileSync(
1773
1838
  'git',
1774
- ['commit', '--no-verify', '-m', `sillyspec: baseline checkpoint for ${changeName}`],
1839
+ ['commit', '--no-verify', '-m', `sillyspec: baseline checkpoint for ${changeName}${body}`],
1775
1840
  { cwd: worktreePath, encoding: 'utf8', stdio: ['pipe','pipe','pipe'], env }
1776
1841
  );
1777
1842
  const hash = git(worktreePath, ['rev-parse', 'HEAD']);
@@ -26,4 +26,4 @@ TaskCard 格式规则(必须严格遵守):
26
26
  - **规范约定字段**(应填但不硬校验,缺失不阻断完成、只影响规范性):author、created_at、priority、depends_on、blocks
27
27
  - allowed_paths 非空(至少一个真实源文件路径;回归类 task 无源码改动时填被验证的关键入口文件)
28
28
  - acceptance 与 verify 都在 frontmatter 内(漏写会被 plan-postcheck 报「缺少验收标准」/「缺少验证步骤」)
29
- - **生成方式**:用 `sillyspec taskcard <change-name> --task task-NN [--all]` 生成安全骨架(LF 行尾 + frontmatter 必闭合 + 九个硬校验字段齐全 + author/created_at 自动填),再往骨架里填语义内容——**勿用 Write tool 整文件手写**(CRLF / 漏闭合 `---` / 漏字段的根源,postcheck 报错大多由此来);已存在的卡命令会跳过不覆盖,局部修改用 Edit 定点改
29
+ - **生成方式**:由主 agent 一次性 `sillyspec taskcard <change-name> --all` 预生成全部安全骨架(LF 行尾 + frontmatter 必闭合 + 九个硬校验字段齐全 + author/created_at 自动填;幂等,已存在跳过)。**子代理不要并行跑 taskcard CLI**(2026-08-25 实证撞进度库 SQLite 锁),只往骨架里填语义内容(LF 行尾 + frontmatter 必闭合 + 九个硬校验字段齐全 + author/created_at 自动填),再往骨架里填语义内容——**勿用 Write tool 整文件手写**(CRLF / 漏闭合 `---` / 漏字段的根源,postcheck 报错大多由此来);已存在的卡命令会跳过不覆盖,局部修改用 Edit 定点改