sillyspec 3.27.2 → 3.27.5

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 (221) hide show
  1. package/.claude/skills/sillyspec-execute/SKILL.md +8 -0
  2. package/.claude/skills/sillyspec-quick/SKILL.md +2 -1
  3. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-archive/SKILL.md +87 -0
  4. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-auto/SKILL.md +93 -0
  5. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
  6. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-commit/SKILL.md +67 -0
  7. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-continue/SKILL.md +47 -0
  8. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
  9. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-execute/SKILL.md +214 -0
  10. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-explore/SKILL.md +102 -0
  11. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-export/SKILL.md +57 -0
  12. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-init/SKILL.md +177 -0
  13. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
  14. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-plan/SKILL.md +156 -0
  15. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-quick/SKILL.md +116 -0
  16. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-resume/SKILL.md +70 -0
  17. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-scan/SKILL.md +79 -0
  18. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-state/SKILL.md +64 -0
  19. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-status/SKILL.md +54 -0
  20. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-verify/SKILL.md +87 -0
  21. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
  22. package/.claude/worktrees/sillyspec-data-model-research/.gitattributes +2 -0
  23. package/.claude/worktrees/sillyspec-data-model-research/.husky/pre-push +20 -0
  24. package/.claude/worktrees/sillyspec-data-model-research/README.md +203 -0
  25. package/.claude/worktrees/sillyspec-data-model-research/SKILL.md +91 -0
  26. package/.claude/worktrees/sillyspec-data-model-research/bin/sillyspec.js +2 -0
  27. package/.claude/worktrees/sillyspec-data-model-research/logo.jpg +0 -0
  28. package/.claude/worktrees/sillyspec-data-model-research/package-lock.json +977 -0
  29. package/.claude/worktrees/sillyspec-data-model-research/package.json +39 -0
  30. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
  31. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
  32. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/favicon.jpg +0 -0
  33. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/index.html +16 -0
  34. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/logo.jpg +0 -0
  35. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-dashboard.html +836 -0
  36. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-overview.html +256 -0
  37. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/package.json +25 -0
  38. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/executor.js +95 -0
  39. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/index.js +679 -0
  40. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/parser.js +526 -0
  41. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/watcher.js +357 -0
  42. package/.claude/worktrees/sillyspec-data-model-research/src/agent-session-log.js +803 -0
  43. package/.claude/worktrees/sillyspec-data-model-research/src/change-list.js +290 -0
  44. package/.claude/worktrees/sillyspec-data-model-research/src/change-risk-profile.js +270 -0
  45. package/.claude/worktrees/sillyspec-data-model-research/src/check-primitives.js +65 -0
  46. package/.claude/worktrees/sillyspec-data-model-research/src/classify-change.js +109 -0
  47. package/.claude/worktrees/sillyspec-data-model-research/src/commit-suggest.js +202 -0
  48. package/.claude/worktrees/sillyspec-data-model-research/src/config-cat.js +88 -0
  49. package/.claude/worktrees/sillyspec-data-model-research/src/config-schema.js +328 -0
  50. package/.claude/worktrees/sillyspec-data-model-research/src/constants.js +97 -0
  51. package/.claude/worktrees/sillyspec-data-model-research/src/contract-matrix.js +480 -0
  52. package/.claude/worktrees/sillyspec-data-model-research/src/datetime.js +20 -0
  53. package/.claude/worktrees/sillyspec-data-model-research/src/db-engine.js +55 -0
  54. package/.claude/worktrees/sillyspec-data-model-research/src/db.js +381 -0
  55. package/.claude/worktrees/sillyspec-data-model-research/src/decision-distill.js +517 -0
  56. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/local-agent.js +155 -0
  57. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/sillyhub-mcp.js +245 -0
  58. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/probe.js +246 -0
  59. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/strategy.js +121 -0
  60. package/.claude/worktrees/sillyspec-data-model-research/src/docs-check.js +825 -0
  61. package/.claude/worktrees/sillyspec-data-model-research/src/docs-debt.js +393 -0
  62. package/.claude/worktrees/sillyspec-data-model-research/src/docs-gate.js +107 -0
  63. package/.claude/worktrees/sillyspec-data-model-research/src/doctor-diagnostics.js +1099 -0
  64. package/.claude/worktrees/sillyspec-data-model-research/src/endpoint-extractor.js +420 -0
  65. package/.claude/worktrees/sillyspec-data-model-research/src/foreign-declared.js +78 -0
  66. package/.claude/worktrees/sillyspec-data-model-research/src/fs-atomic.js +73 -0
  67. package/.claude/worktrees/sillyspec-data-model-research/src/git-helper.js +150 -0
  68. package/.claude/worktrees/sillyspec-data-model-research/src/hooks/claude-pre-tool-use.cjs +130 -0
  69. package/.claude/worktrees/sillyspec-data-model-research/src/hooks/worktree-guard.js +892 -0
  70. package/.claude/worktrees/sillyspec-data-model-research/src/index.js +2798 -0
  71. package/.claude/worktrees/sillyspec-data-model-research/src/init.js +626 -0
  72. package/.claude/worktrees/sillyspec-data-model-research/src/knowledge-match.js +231 -0
  73. package/.claude/worktrees/sillyspec-data-model-research/src/local-detect.js +128 -0
  74. package/.claude/worktrees/sillyspec-data-model-research/src/local-register.js +104 -0
  75. package/.claude/worktrees/sillyspec-data-model-research/src/machine-interface.js +543 -0
  76. package/.claude/worktrees/sillyspec-data-model-research/src/migrate.js +127 -0
  77. package/.claude/worktrees/sillyspec-data-model-research/src/module-impact.js +166 -0
  78. package/.claude/worktrees/sillyspec-data-model-research/src/module-resolve.js +222 -0
  79. package/.claude/worktrees/sillyspec-data-model-research/src/modules.js +580 -0
  80. package/.claude/worktrees/sillyspec-data-model-research/src/progress/change-registry.js +336 -0
  81. package/.claude/worktrees/sillyspec-data-model-research/src/progress/consistency-doctor.js +355 -0
  82. package/.claude/worktrees/sillyspec-data-model-research/src/progress/shared.js +36 -0
  83. package/.claude/worktrees/sillyspec-data-model-research/src/progress/stage-machine.js +594 -0
  84. package/.claude/worktrees/sillyspec-data-model-research/src/progress/step-store.js +265 -0
  85. package/.claude/worktrees/sillyspec-data-model-research/src/progress.js +1304 -0
  86. package/.claude/worktrees/sillyspec-data-model-research/src/quick-recommend.js +115 -0
  87. package/.claude/worktrees/sillyspec-data-model-research/src/quicklog.js +852 -0
  88. package/.claude/worktrees/sillyspec-data-model-research/src/review-tier.js +77 -0
  89. package/.claude/worktrees/sillyspec-data-model-research/src/run/command.js +1595 -0
  90. package/.claude/worktrees/sillyspec-data-model-research/src/run/complete-handlers.js +1552 -0
  91. package/.claude/worktrees/sillyspec-data-model-research/src/run/complete.js +1259 -0
  92. package/.claude/worktrees/sillyspec-data-model-research/src/run/concurrent-detect.js +156 -0
  93. package/.claude/worktrees/sillyspec-data-model-research/src/run/gates.js +1084 -0
  94. package/.claude/worktrees/sillyspec-data-model-research/src/run/multi-repo-context.js +208 -0
  95. package/.claude/worktrees/sillyspec-data-model-research/src/run/next.js +128 -0
  96. package/.claude/worktrees/sillyspec-data-model-research/src/run/prompt.js +843 -0
  97. package/.claude/worktrees/sillyspec-data-model-research/src/run/quick-audit.js +186 -0
  98. package/.claude/worktrees/sillyspec-data-model-research/src/run/scan-profile.js +269 -0
  99. package/.claude/worktrees/sillyspec-data-model-research/src/run/shared.js +1562 -0
  100. package/.claude/worktrees/sillyspec-data-model-research/src/run/stage.js +584 -0
  101. package/.claude/worktrees/sillyspec-data-model-research/src/run.js +23 -0
  102. package/.claude/worktrees/sillyspec-data-model-research/src/scan-diff.js +388 -0
  103. package/.claude/worktrees/sillyspec-data-model-research/src/scan-postcheck.js +512 -0
  104. package/.claude/worktrees/sillyspec-data-model-research/src/scan-staleness.js +95 -0
  105. package/.claude/worktrees/sillyspec-data-model-research/src/setup.js +414 -0
  106. package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/client.js +597 -0
  107. package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/config.js +62 -0
  108. package/.claude/worktrees/sillyspec-data-model-research/src/spec-dir-typo.js +50 -0
  109. package/.claude/worktrees/sillyspec-data-model-research/src/spec-sync.js +358 -0
  110. package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-engine.js +171 -0
  111. package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-spec.js +550 -0
  112. package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract.js +889 -0
  113. package/.claude/worktrees/sillyspec-data-model-research/src/stage-review.js +597 -0
  114. package/.claude/worktrees/sillyspec-data-model-research/src/stages/archive.js +199 -0
  115. package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm-auto.js +236 -0
  116. package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm.js +617 -0
  117. package/.claude/worktrees/sillyspec-data-model-research/src/stages/cmd-existence.js +145 -0
  118. package/.claude/worktrees/sillyspec-data-model-research/src/stages/doctor.js +540 -0
  119. package/.claude/worktrees/sillyspec-data-model-research/src/stages/execute.js +1275 -0
  120. package/.claude/worktrees/sillyspec-data-model-research/src/stages/explore.js +43 -0
  121. package/.claude/worktrees/sillyspec-data-model-research/src/stages/index.js +29 -0
  122. package/.claude/worktrees/sillyspec-data-model-research/src/stages/knowledge.js +514 -0
  123. package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan-postcheck.js +1344 -0
  124. package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan.js +650 -0
  125. package/.claude/worktrees/sillyspec-data-model-research/src/stages/quick.js +167 -0
  126. package/.claude/worktrees/sillyspec-data-model-research/src/stages/scan.js +608 -0
  127. package/.claude/worktrees/sillyspec-data-model-research/src/stages/status.js +64 -0
  128. package/.claude/worktrees/sillyspec-data-model-research/src/stages/verify.js +246 -0
  129. package/.claude/worktrees/sillyspec-data-model-research/src/sync.js +1470 -0
  130. package/.claude/worktrees/sillyspec-data-model-research/src/task-review.js +1498 -0
  131. package/.claude/worktrees/sillyspec-data-model-research/src/taskcard.js +210 -0
  132. package/.claude/worktrees/sillyspec-data-model-research/src/verify-postcheck.js +1718 -0
  133. package/.claude/worktrees/sillyspec-data-model-research/src/verify-probes.js +277 -0
  134. package/.claude/worktrees/sillyspec-data-model-research/src/version.js +19 -0
  135. package/.claude/worktrees/sillyspec-data-model-research/src/workflow.js +600 -0
  136. package/.claude/worktrees/sillyspec-data-model-research/src/workspace.js +125 -0
  137. package/.claude/worktrees/sillyspec-data-model-research/src/worktree-apply.js +1446 -0
  138. package/.claude/worktrees/sillyspec-data-model-research/src/worktree-deps.js +576 -0
  139. package/.claude/worktrees/sillyspec-data-model-research/src/worktree.js +1715 -0
  140. package/.claude/worktrees/sillyspec-data-model-research/templates/claude-instruction.md +23 -0
  141. package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/taskcard-rules.md +28 -0
  142. package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/testcase-design.md +12 -0
  143. package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/verify-probes.md +32 -0
  144. package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/archive-impact.yaml +79 -0
  145. package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/scan-docs.yaml +132 -0
  146. package/eval/README.md +86 -0
  147. package/eval/eval.config.json +28 -0
  148. package/eval/lib/adapters/cli.mjs +63 -0
  149. package/eval/lib/adapters/dryrun.mjs +27 -0
  150. package/eval/lib/config.mjs +82 -0
  151. package/eval/lib/db.mjs +76 -0
  152. package/eval/lib/report.mjs +120 -0
  153. package/eval/lib/runner.mjs +157 -0
  154. package/eval/lib/tasks.mjs +73 -0
  155. package/eval/lib/util.mjs +107 -0
  156. package/eval/lib/workdir.mjs +30 -0
  157. package/eval/run.mjs +83 -0
  158. package/eval/tasks/001-slugify/files/package.json +6 -0
  159. package/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
  160. package/eval/tasks/001-slugify/task.json +6 -0
  161. package/eval/tasks/001-slugify/verify.mjs +28 -0
  162. package/eval/tasks/002-each-day/files/package.json +6 -0
  163. package/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
  164. package/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
  165. package/eval/tasks/002-each-day/task.json +6 -0
  166. package/eval/tasks/002-each-day/verify.mjs +31 -0
  167. package/eval/tasks/003-word-report/files/data/notes.txt +5 -0
  168. package/eval/tasks/003-word-report/files/package.json +6 -0
  169. package/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
  170. package/eval/tasks/003-word-report/task.json +6 -0
  171. package/eval/tasks/003-word-report/verify.mjs +38 -0
  172. package/package.json +4 -2
  173. package/src/agent-session-log.js +45 -5
  174. package/src/change-risk-profile.js +83 -14
  175. package/src/config-schema.js +18 -3
  176. package/src/contract-matrix.js +90 -29
  177. package/src/datetime.js +20 -0
  178. package/src/decision-distill.js +517 -0
  179. package/src/dispatch/backends/local-agent.js +4 -4
  180. package/src/docs-check.js +227 -0
  181. package/src/docs-debt.js +187 -24
  182. package/src/doctor-diagnostics.js +102 -2
  183. package/src/foreign-declared.js +78 -0
  184. package/src/index.js +89 -9
  185. package/src/init.js +9 -7
  186. package/src/knowledge-match.js +107 -6
  187. package/src/local-register.js +7 -1
  188. package/src/module-resolve.js +12 -1
  189. package/src/modules.js +2 -1
  190. package/src/plan-adopt-waves.js +126 -0
  191. package/src/quicklog.js +50 -4
  192. package/src/run/command.js +122 -43
  193. package/src/run/complete-handlers.js +48 -19
  194. package/src/run/complete.js +5 -2
  195. package/src/run/gates.js +87 -22
  196. package/src/run/prompt.js +84 -42
  197. package/src/run/quick-audit.js +11 -0
  198. package/src/run/shared.js +152 -10
  199. package/src/run.js +2 -2
  200. package/src/scan-postcheck.js +30 -17
  201. package/src/stage-contract-spec.js +2 -1
  202. package/src/stage-contract.js +68 -4
  203. package/src/stages/archive.js +53 -12
  204. package/src/stages/brainstorm-auto.js +7 -0
  205. package/src/stages/brainstorm.js +11 -0
  206. package/src/stages/doctor.js +157 -1
  207. package/src/stages/execute.js +32 -6
  208. package/src/stages/plan-postcheck.js +76 -17
  209. package/src/stages/plan.js +1 -0
  210. package/src/stages/quick.js +6 -1
  211. package/src/stages/verify.js +31 -14
  212. package/src/sync.js +41 -3
  213. package/src/task-review.js +2 -2
  214. package/src/taskcard-placeholders.js +21 -0
  215. package/src/taskcard.js +10 -1
  216. package/src/verify-postcheck.js +315 -13
  217. package/src/verify-probes.js +46 -4
  218. package/src/worktree-apply.js +184 -7
  219. package/src/worktree-deps.js +57 -0
  220. package/src/worktree.js +201 -58
  221. package/templates/prompts/taskcard-rules.md +1 -0
@@ -9,8 +9,9 @@
9
9
  */
10
10
 
11
11
  import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'fs'
12
- import { join, resolve, basename, relative } from 'path'
12
+ import { join, resolve, basename, dirname, relative } from 'path'
13
13
  import { gitQuiet } from './git-helper.js'
14
+ import { splitOwnVsForeignDiffFiles } from './foreign-declared.js'
14
15
  import {
15
16
  scanBackendEndpoints,
16
17
  scanFrontendApiCalls,
@@ -333,26 +334,50 @@ export function buildContractFieldInjection(changeDir, taskName) {
333
334
 
334
335
  // ─── Verify 阶段:parity check ──────────────────────────────────────────
335
336
 
337
+ /**
338
+ * 读 worktree meta(parity 双根扫描 / diff 收窄共用)。
339
+ * 优先 specBase/.runtime/worktrees/<change>/meta.json(主仓 spec 根——cwd=worktree 时 specBase 已被
340
+ * command.js 漂移守卫锚定主仓;旧实现硬编码 scanRoot/.sillyspec/.runtime,worktree 内运行永远读不到
341
+ * meta);兼容回退 scanRoot/.sillyspec/.runtime(旧路径)。
342
+ * @returns {{ meta: object, gitDir: string }|null} gitDir = diff/扫描用 git 根(真实 worktree 或 scanRoot)
343
+ */
344
+ function _readWorktreeMeta(specBase, scanRoot, changeName) {
345
+ if (!changeName) return null
346
+ const candidates = [
347
+ specBase ? join(specBase, '.runtime', 'worktrees', changeName, 'meta.json') : null,
348
+ join(scanRoot, '.sillyspec', '.runtime', 'worktrees', changeName, 'meta.json'),
349
+ ].filter(Boolean)
350
+ for (const metaPath of candidates) {
351
+ try {
352
+ if (!existsSync(metaPath)) continue
353
+ const meta = JSON.parse(readFileSync(metaPath, 'utf8'))
354
+ const gitDir = (meta.worktreePath && meta.mode !== 'in-place-fallback' && existsSync(meta.worktreePath))
355
+ ? meta.worktreePath : scanRoot
356
+ return { meta, gitDir }
357
+ } catch { /* 损坏 meta 试下一候选 */ }
358
+ }
359
+ return null
360
+ }
361
+
336
362
  /**
337
363
  * 取本变更 diff 文件集(parity 前端调用收窄用,坑 probe5-fullrepo-frontend-noise)。
338
364
  * 优先 worktree meta 锚点 diff(与 verify 同源);无 meta 退主仓 git diff HEAD。
339
365
  * @returns {string[]|null}
340
366
  */
341
- function _resolveDiffFilesForParity(scanRoot, changeName) {
367
+ function _resolveDiffFilesForParity(specBase, scanRoot, changeName) {
342
368
  try {
343
- const metaPath = join(scanRoot, '.sillyspec', '.runtime', 'worktrees', changeName, 'meta.json')
344
- if (existsSync(metaPath)) {
345
- const meta = JSON.parse(readFileSync(metaPath, 'utf8'))
346
- const diffBase = meta.baselineCommit || meta.actualBaseHash || meta.baseHash
347
- const gitDir = (meta.worktreePath && meta.mode !== 'in-place-fallback' && existsSync(meta.worktreePath))
348
- ? meta.worktreePath : scanRoot
369
+ const wt = _readWorktreeMeta(specBase, scanRoot, changeName)
370
+ if (wt) {
371
+ const diffBase = wt.meta.baselineCommit || wt.meta.actualBaseHash || wt.meta.baseHash
349
372
  if (diffBase) {
350
- const out = gitQuiet(gitDir, ['diff', '--name-only', `${diffBase}..HEAD`], { timeout: 30000 })
373
+ const out = gitQuiet(wt.gitDir, ['diff', '--name-only', `${diffBase}..HEAD`], { timeout: 30000 })
351
374
  if (out !== null) {
352
375
  const files = out.split('\n').filter(Boolean)
353
- // 并入 worktree 未提交(子代理默认不 commit 的形态,与 resolveVerifyChangedFiles 同口径)
376
+ // 并入 worktree 未提交(子代理默认不 commit 的形态,与 resolveVerifyChangedFiles 同口径)。
377
+ // -uall:porcelain 默认把整目录折叠成 `dir/` 一项,文件级匹配(前端调用过滤)会漏新目录
378
+ // 里的文件(坑 probe5-worktree-single-root-noise 实证:web/ 折叠 → feature.js 漏匹配)
354
379
  try {
355
- const wtStatus = gitQuiet(gitDir, ['status', '--porcelain'], { timeout: 30000, trim: false })
380
+ const wtStatus = gitQuiet(wt.gitDir, ['status', '--porcelain', '--untracked-files=all'], { timeout: 30000, trim: false })
356
381
  for (const line of String(wtStatus || '').split('\n')) {
357
382
  if (!line || line.length < 4) continue
358
383
  const p = line.slice(3).trim().split(' -> ').pop() || ''
@@ -364,9 +389,16 @@ function _resolveDiffFilesForParity(scanRoot, changeName) {
364
389
  }
365
390
  }
366
391
  }
367
- // 无 meta(brownfield/已 cleanup):主仓未提交 diff
392
+ // 无 meta(brownfield/已 cleanup):主仓未提交 diff。他者声明归属过滤(坑
393
+ // verify-reconcile-foreign-wip):并行会话在途 WIP 的前端文件混入会产出「他者调用 ×
394
+ // 本变更端点」的 missingBackend 误报——剔除他者显式声明文件(fail-closed:无主保留)。
368
395
  const out = gitQuiet(scanRoot, ['diff', '--name-only', 'HEAD'], { timeout: 30000 })
369
- return out !== null ? out.split('\n').filter(Boolean) : null
396
+ if (out === null) return null
397
+ const { own, foreign } = splitOwnVsForeignDiffFiles(scanRoot, changeName, out.split('\n').filter(Boolean))
398
+ if (foreign.length > 0) {
399
+ console.warn(`⚠️ parity 对账已排除 ${foreign.length} 个并行会话声明的文件(${foreign.slice(0, 5).map(x => x.file).join(', ')}${foreign.length > 5 ? ' 等' : ''})`)
400
+ }
401
+ return own
370
402
  } catch { return null }
371
403
  }
372
404
 
@@ -410,18 +442,42 @@ export function verifyApiParity(specBase, scanRoot, runtimeRoot, changeName = nu
410
442
  return { ok: true, missingBackend: [], unusedBackend: [], summary: 'No scan root for parity check', backendCount: allProviderEndpoints.length, frontendCount: 0 }
411
443
  }
412
444
 
413
- // ── 现算端点并入基线(坑 probe5-endpoint-baseline-stale,2026-08-22 实证:endpoints 基线
414
- // 是 execute 时落的存量 artifacts,verify 时主仓已被并行会话/部署扰动推进——存量基线与当前
415
- // 代码失配 → 前端调用(当前代码)对不上旧端点集 → missingBackend 全量误报。现算当前代码的
416
- // 后端端点并入比对集:missingBackend 判定以「当前真实代码 ∪ 存量基线」为准(现算覆盖大部分、
417
- // 存量补充跨仓/已删代码);unusedBackend 只对现算端点算(存量过期端点不再误报 unused)。
418
- let liveEndpoints = []
419
- try { liveEndpoints = scanBackendEndpoints(scanRoot) } catch { /* 现算失败退回纯存量基线 */ }
445
+ // ── 双根并集现算(坑 probe5-worktree-single-root-noise,2026-08-24 用户实证:worktree 场景
446
+ // 只现算 scanRoot 单根,另一侧端点不在比对集——verify 从主仓跑(apply 前)漏 worktree 新增端点;
447
+ // verify 从 worktree 跑则既读不到 meta(旧路径硬编码 scanRoot/.sillyspec)也漏主仓既有 daemon
448
+ // 端点 → 前端调用全量误报 missing)。后端端点全集 = 主仓既有 ∪ worktree 新增 ∪ 存量 artifact:
449
+ // 现算根集 = [scanRoot, meta.worktreePath, 主仓根(cwd 自身在 linked worktree 内时经
450
+ // git-common-dir 解析)],逐根 scanBackendEndpoints 按 method+path 去重。
451
+ // 存量基线失配坑(probe5-endpoint-baseline-stale,2026-08-22)由本并集一并覆盖:现算当前
452
+ // 代码 ∪ 存量基线;unusedBackend 只对现算端点算(存量过期端点不再误报 unused)。
453
+ const wt = _readWorktreeMeta(specBase, scanRoot, changeName)
454
+ const liveRoots = []
455
+ const pushRoot = (root, label) => {
456
+ if (!root || !existsSync(root)) return
457
+ const abs = resolve(root)
458
+ if (liveRoots.some(r => resolve(r.root) === abs)) return
459
+ liveRoots.push({ root, label })
460
+ }
461
+ pushRoot(scanRoot, 'scan-root')
462
+ if (wt && wt.gitDir !== scanRoot) pushRoot(wt.gitDir, 'worktree')
463
+ // cwd 自身是 linked worktree 时补主仓根(主仓返回 .git → dirname=scanRoot 被 pushRoot 去重)
464
+ try {
465
+ const commonDir = gitQuiet(scanRoot, ['rev-parse', '--git-common-dir'])
466
+ if (commonDir) pushRoot(dirname(resolve(scanRoot, commonDir)), 'main')
467
+ } catch { /* 非 git 目录/解析失败:单根继续 */ }
468
+
469
+ const liveByRoot = []
470
+ for (const { root, label } of liveRoots) {
471
+ try { liveByRoot.push({ root, label, endpoints: scanBackendEndpoints(root) }) } catch { /* 单根失败不拖垮整体 */ }
472
+ }
473
+ const liveEndpoints = liveByRoot.flatMap(r => r.endpoints)
420
474
  const liveKeys = new Set(liveEndpoints.map(e => `${e.method} ${e.path}`))
475
+ const mergedKeys = new Set(allProviderEndpoints.map(e => `${e.method} ${e.path}`))
421
476
  const mergedProviderEndpoints = [...allProviderEndpoints]
422
477
  for (const e of liveEndpoints) {
423
478
  const key = `${e.method} ${e.path}`
424
- if (!allProviderEndpoints.some(p => `${p.method} ${p.path}` === key)) {
479
+ if (!mergedKeys.has(key)) {
480
+ mergedKeys.add(key)
425
481
  mergedProviderEndpoints.push(e)
426
482
  }
427
483
  }
@@ -431,24 +487,28 @@ export function verifyApiParity(specBase, scanRoot, runtimeRoot, changeName = nu
431
487
  // → 143 个 missing 全是误报噪音)。比对口径 = 本变更 diff 文件内的调用 ∪ 全仓(仅当
432
488
  // 端点登记源也是全仓时——无 changeName 的 CLI contractScan 场景)。diff 不可得时退回
433
489
  // 全仓(不静默跳过对账)。
490
+ // 扫描根优先真实 worktree(坑 probe5-worktree-single-root-noise):apply 前新代码只在
491
+ // worktree,scanRoot(主仓 cwd)里是旧版本——读旧版会漏新调用/多已删调用。
434
492
  let frontendCalls = []
435
493
  let frontendScope = 'full-repo'
494
+ const frontendRoot = (wt && wt.gitDir !== scanRoot) ? wt.gitDir : scanRoot
495
+ const frontendRootLabel = frontendRoot === scanRoot ? 'scan-root' : 'worktree'
436
496
  if (changeName) {
437
497
  try {
438
- const changed = _resolveDiffFilesForParity(scanRoot, changeName)
498
+ const changed = _resolveDiffFilesForParity(specBase, scanRoot, changeName)
439
499
  if (changed && changed.length > 0) {
440
500
  const changedSet = new Set(changed.map(f => f.replace(/\\/g, '/')))
441
- frontendCalls = scanFrontendApiCalls(scanRoot).filter(c => {
501
+ frontendCalls = scanFrontendApiCalls(frontendRoot).filter(c => {
442
502
  const src = String(c.source || '').replace(/\\/g, '/')
443
503
  return changedSet.has(src) || [...changedSet].some(cf => src.endsWith(cf) || cf.endsWith(src))
444
504
  })
445
- frontendScope = `change-diff (${changed.length} files)`
505
+ frontendScope = `change-diff (${changed.length} files @ ${frontendRootLabel})`
446
506
  } else {
447
- frontendCalls = scanFrontendApiCalls(scanRoot)
507
+ frontendCalls = scanFrontendApiCalls(frontendRoot)
448
508
  }
449
- } catch { frontendCalls = scanFrontendApiCalls(scanRoot) }
509
+ } catch { frontendCalls = scanFrontendApiCalls(frontendRoot) }
450
510
  } else {
451
- frontendCalls = scanFrontendApiCalls(scanRoot)
511
+ frontendCalls = scanFrontendApiCalls(frontendRoot)
452
512
  }
453
513
 
454
514
  const { missingBackend, unusedBackend } = diffApiParity(frontendCalls, mergedProviderEndpoints)
@@ -460,13 +520,14 @@ export function verifyApiParity(specBase, scanRoot, runtimeRoot, changeName = nu
460
520
  })
461
521
 
462
522
  const ok = missingBackend.length === 0
523
+ const liveRootSummary = liveByRoot.map(r => `${r.label} ${r.endpoints.length}`).join(' + ')
463
524
  let summary = ok
464
- ? `✅ API parity check passed: ${mergedProviderEndpoints.length} backend endpoints (live ${liveEndpoints.length} + artifact ${allProviderEndpoints.length}), ${frontendCalls.length} frontend calls [scope: ${frontendScope}]`
525
+ ? `✅ API parity check passed: ${mergedProviderEndpoints.length} backend endpoints (live [${liveRootSummary}] + artifact ${allProviderEndpoints.length}), ${frontendCalls.length} frontend calls [scope: ${frontendScope}]`
465
526
  : `❌ API parity check failed: ${missingBackend.length} frontend calls have no matching backend endpoint [scope: ${frontendScope}]`
466
527
 
467
528
  if (narrowedUnused.length > 0) {
468
529
  summary += ` | ${narrowedUnused.length} backend endpoints unused by frontend`
469
530
  }
470
531
 
471
- return { ok, missingBackend, unusedBackend: narrowedUnused, summary, backendCount: mergedProviderEndpoints.length, frontendCount: frontendCalls.length }
532
+ return { ok, missingBackend, unusedBackend: narrowedUnused, summary, backendCount: mergedProviderEndpoints.length, frontendCount: frontendCalls.length, scanRoots: liveByRoot.map(r => r.root) }
472
533
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 人读墙钟时间工具(坑 taskcard-created-at-utc,2026-08-23 实证:taskcard 骨架 created_at 用
3
+ * toISOString() 落 UTC——本地 09:39 生成写 01:39,子代理两次手工改)。
4
+ *
5
+ * 项目内「人读的 markdown/YAML frontmatter 时间字段」统一走本函数:本地时区 +
6
+ * `YYYY-MM-DD HH:mm:ss` 形状(scan.js 文档示例形状),手工拼接零 locale/ICU 依赖(Node
7
+ * small-icu 构建下 toLocaleString('sv-SE') 不可靠)。机器可读处(JSON/DB 列/目录名)继续用
8
+ * toISOString()——那是 ISO 惯例不是坑。
9
+ */
10
+
11
+ /**
12
+ * 本地墙钟时间字符串(YYYY-MM-DD HH:mm:ss,各段补零)。
13
+ * @param {Date} [d] 可注入时钟(测试用),缺省当前时间
14
+ * @returns {string}
15
+ */
16
+ export function nowWallClock(d = new Date()) {
17
+ const p = (n) => String(n).padStart(2, '0');
18
+ return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} `
19
+ + `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
20
+ }