sillyspec 3.27.2 → 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 (398) 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/.worktrees/8d2d0392/.claude/skills/sillyspec-archive/SKILL.md +87 -0
  147. package/.worktrees/8d2d0392/.claude/skills/sillyspec-auto/SKILL.md +93 -0
  148. package/.worktrees/8d2d0392/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
  149. package/.worktrees/8d2d0392/.claude/skills/sillyspec-commit/SKILL.md +67 -0
  150. package/.worktrees/8d2d0392/.claude/skills/sillyspec-continue/SKILL.md +47 -0
  151. package/.worktrees/8d2d0392/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
  152. package/.worktrees/8d2d0392/.claude/skills/sillyspec-execute/SKILL.md +214 -0
  153. package/.worktrees/8d2d0392/.claude/skills/sillyspec-explore/SKILL.md +102 -0
  154. package/.worktrees/8d2d0392/.claude/skills/sillyspec-export/SKILL.md +57 -0
  155. package/.worktrees/8d2d0392/.claude/skills/sillyspec-init/SKILL.md +177 -0
  156. package/.worktrees/8d2d0392/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
  157. package/.worktrees/8d2d0392/.claude/skills/sillyspec-plan/SKILL.md +156 -0
  158. package/.worktrees/8d2d0392/.claude/skills/sillyspec-propose/SKILL.md +87 -0
  159. package/.worktrees/8d2d0392/.claude/skills/sillyspec-quick/SKILL.md +116 -0
  160. package/.worktrees/8d2d0392/.claude/skills/sillyspec-resume/SKILL.md +70 -0
  161. package/.worktrees/8d2d0392/.claude/skills/sillyspec-scan/SKILL.md +79 -0
  162. package/.worktrees/8d2d0392/.claude/skills/sillyspec-state/SKILL.md +64 -0
  163. package/.worktrees/8d2d0392/.claude/skills/sillyspec-status/SKILL.md +54 -0
  164. package/.worktrees/8d2d0392/.claude/skills/sillyspec-verify/SKILL.md +87 -0
  165. package/.worktrees/8d2d0392/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
  166. package/.worktrees/8d2d0392/.claude/skills/verify-per-user/SKILL.md +6 -0
  167. package/.worktrees/8d2d0392/.gitattributes +2 -0
  168. package/.worktrees/8d2d0392/.husky/pre-push +20 -0
  169. package/.worktrees/8d2d0392/README.md +203 -0
  170. package/.worktrees/8d2d0392/SKILL.md +91 -0
  171. package/.worktrees/8d2d0392/architecture-analysis.md +448 -0
  172. package/.worktrees/8d2d0392/bin/sillyspec.js +2 -0
  173. package/.worktrees/8d2d0392/eval/README.md +86 -0
  174. package/.worktrees/8d2d0392/eval/eval.config.json +28 -0
  175. package/.worktrees/8d2d0392/eval/lib/adapters/cli.mjs +63 -0
  176. package/.worktrees/8d2d0392/eval/lib/adapters/dryrun.mjs +27 -0
  177. package/.worktrees/8d2d0392/eval/lib/config.mjs +82 -0
  178. package/.worktrees/8d2d0392/eval/lib/db.mjs +76 -0
  179. package/.worktrees/8d2d0392/eval/lib/report.mjs +120 -0
  180. package/.worktrees/8d2d0392/eval/lib/runner.mjs +157 -0
  181. package/.worktrees/8d2d0392/eval/lib/tasks.mjs +73 -0
  182. package/.worktrees/8d2d0392/eval/lib/util.mjs +107 -0
  183. package/.worktrees/8d2d0392/eval/lib/workdir.mjs +30 -0
  184. package/.worktrees/8d2d0392/eval/run.mjs +83 -0
  185. package/.worktrees/8d2d0392/eval/tasks/001-slugify/files/package.json +6 -0
  186. package/.worktrees/8d2d0392/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
  187. package/.worktrees/8d2d0392/eval/tasks/001-slugify/task.json +6 -0
  188. package/.worktrees/8d2d0392/eval/tasks/001-slugify/verify.mjs +28 -0
  189. package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/package.json +6 -0
  190. package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
  191. package/.worktrees/8d2d0392/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
  192. package/.worktrees/8d2d0392/eval/tasks/002-each-day/task.json +6 -0
  193. package/.worktrees/8d2d0392/eval/tasks/002-each-day/verify.mjs +31 -0
  194. package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/data/notes.txt +5 -0
  195. package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/package.json +6 -0
  196. package/.worktrees/8d2d0392/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
  197. package/.worktrees/8d2d0392/eval/tasks/003-word-report/task.json +6 -0
  198. package/.worktrees/8d2d0392/eval/tasks/003-word-report/verify.mjs +38 -0
  199. package/.worktrees/8d2d0392/logo.jpg +0 -0
  200. package/.worktrees/8d2d0392/package-lock.json +977 -0
  201. package/.worktrees/8d2d0392/package.json +41 -0
  202. package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
  203. package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
  204. package/.worktrees/8d2d0392/packages/dashboard/dist/favicon.jpg +0 -0
  205. package/.worktrees/8d2d0392/packages/dashboard/dist/index.html +16 -0
  206. package/.worktrees/8d2d0392/packages/dashboard/dist/logo.jpg +0 -0
  207. package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-dashboard.html +836 -0
  208. package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-overview.html +256 -0
  209. package/.worktrees/8d2d0392/packages/dashboard/package.json +25 -0
  210. package/.worktrees/8d2d0392/packages/dashboard/server/executor.js +95 -0
  211. package/.worktrees/8d2d0392/packages/dashboard/server/index.js +679 -0
  212. package/.worktrees/8d2d0392/packages/dashboard/server/parser.js +526 -0
  213. package/.worktrees/8d2d0392/packages/dashboard/server/watcher.js +357 -0
  214. package/.worktrees/8d2d0392/src/agent-session-log.js +803 -0
  215. package/.worktrees/8d2d0392/src/change-list.js +290 -0
  216. package/.worktrees/8d2d0392/src/change-risk-profile.js +339 -0
  217. package/.worktrees/8d2d0392/src/check-primitives.js +65 -0
  218. package/.worktrees/8d2d0392/src/classify-change.js +109 -0
  219. package/.worktrees/8d2d0392/src/commit-suggest.js +202 -0
  220. package/.worktrees/8d2d0392/src/config-cat.js +88 -0
  221. package/.worktrees/8d2d0392/src/config-schema.js +328 -0
  222. package/.worktrees/8d2d0392/src/constants.js +97 -0
  223. package/.worktrees/8d2d0392/src/contract-matrix.js +533 -0
  224. package/.worktrees/8d2d0392/src/datetime.js +20 -0
  225. package/.worktrees/8d2d0392/src/db-engine.js +55 -0
  226. package/.worktrees/8d2d0392/src/db.js +381 -0
  227. package/.worktrees/8d2d0392/src/decision-distill.js +517 -0
  228. package/.worktrees/8d2d0392/src/dispatch/backends/local-agent.js +155 -0
  229. package/.worktrees/8d2d0392/src/dispatch/backends/sillyhub-mcp.js +245 -0
  230. package/.worktrees/8d2d0392/src/dispatch/probe.js +246 -0
  231. package/.worktrees/8d2d0392/src/dispatch/strategy.js +121 -0
  232. package/.worktrees/8d2d0392/src/docs-check.js +825 -0
  233. package/.worktrees/8d2d0392/src/docs-debt.js +393 -0
  234. package/.worktrees/8d2d0392/src/docs-gate.js +107 -0
  235. package/.worktrees/8d2d0392/src/doctor-diagnostics.js +1099 -0
  236. package/.worktrees/8d2d0392/src/endpoint-extractor.js +409 -0
  237. package/.worktrees/8d2d0392/src/foreign-declared.js +78 -0
  238. package/.worktrees/8d2d0392/src/fs-atomic.js +73 -0
  239. package/.worktrees/8d2d0392/src/git-helper.js +150 -0
  240. package/.worktrees/8d2d0392/src/hooks/claude-pre-tool-use.cjs +130 -0
  241. package/.worktrees/8d2d0392/src/hooks/worktree-guard.js +892 -0
  242. package/.worktrees/8d2d0392/src/index.js +2854 -0
  243. package/.worktrees/8d2d0392/src/init.js +626 -0
  244. package/.worktrees/8d2d0392/src/knowledge-match.js +231 -0
  245. package/.worktrees/8d2d0392/src/local-detect.js +128 -0
  246. package/.worktrees/8d2d0392/src/local-register.js +104 -0
  247. package/.worktrees/8d2d0392/src/machine-interface.js +543 -0
  248. package/.worktrees/8d2d0392/src/migrate.js +127 -0
  249. package/.worktrees/8d2d0392/src/module-impact.js +166 -0
  250. package/.worktrees/8d2d0392/src/module-resolve.js +222 -0
  251. package/.worktrees/8d2d0392/src/modules.js +580 -0
  252. package/.worktrees/8d2d0392/src/plan-adopt-waves.js +126 -0
  253. package/.worktrees/8d2d0392/src/progress/change-registry.js +336 -0
  254. package/.worktrees/8d2d0392/src/progress/consistency-doctor.js +355 -0
  255. package/.worktrees/8d2d0392/src/progress/shared.js +36 -0
  256. package/.worktrees/8d2d0392/src/progress/stage-machine.js +594 -0
  257. package/.worktrees/8d2d0392/src/progress/step-store.js +265 -0
  258. package/.worktrees/8d2d0392/src/progress.js +1304 -0
  259. package/.worktrees/8d2d0392/src/quick-recommend.js +115 -0
  260. package/.worktrees/8d2d0392/src/quicklog.js +883 -0
  261. package/.worktrees/8d2d0392/src/review-tier.js +77 -0
  262. package/.worktrees/8d2d0392/src/run/command.js +1641 -0
  263. package/.worktrees/8d2d0392/src/run/complete-handlers.js +1552 -0
  264. package/.worktrees/8d2d0392/src/run/complete.js +1259 -0
  265. package/.worktrees/8d2d0392/src/run/concurrent-detect.js +156 -0
  266. package/.worktrees/8d2d0392/src/run/gates.js +1094 -0
  267. package/.worktrees/8d2d0392/src/run/multi-repo-context.js +208 -0
  268. package/.worktrees/8d2d0392/src/run/next.js +128 -0
  269. package/.worktrees/8d2d0392/src/run/prompt.js +843 -0
  270. package/.worktrees/8d2d0392/src/run/quick-audit.js +186 -0
  271. package/.worktrees/8d2d0392/src/run/scan-profile.js +269 -0
  272. package/.worktrees/8d2d0392/src/run/shared.js +1562 -0
  273. package/.worktrees/8d2d0392/src/run/stage.js +587 -0
  274. package/.worktrees/8d2d0392/src/run.js +23 -0
  275. package/.worktrees/8d2d0392/src/scan-diff.js +388 -0
  276. package/.worktrees/8d2d0392/src/scan-postcheck.js +524 -0
  277. package/.worktrees/8d2d0392/src/scan-staleness.js +95 -0
  278. package/.worktrees/8d2d0392/src/setup.js +414 -0
  279. package/.worktrees/8d2d0392/src/sillyhub-mcp/client.js +597 -0
  280. package/.worktrees/8d2d0392/src/sillyhub-mcp/config.js +62 -0
  281. package/.worktrees/8d2d0392/src/spec-dir-typo.js +50 -0
  282. package/.worktrees/8d2d0392/src/spec-sync.js +358 -0
  283. package/.worktrees/8d2d0392/src/stage-contract-engine.js +171 -0
  284. package/.worktrees/8d2d0392/src/stage-contract-spec.js +551 -0
  285. package/.worktrees/8d2d0392/src/stage-contract.js +948 -0
  286. package/.worktrees/8d2d0392/src/stage-review.js +597 -0
  287. package/.worktrees/8d2d0392/src/stages/archive.js +199 -0
  288. package/.worktrees/8d2d0392/src/stages/brainstorm-auto.js +285 -0
  289. package/.worktrees/8d2d0392/src/stages/brainstorm.js +624 -0
  290. package/.worktrees/8d2d0392/src/stages/cmd-existence.js +145 -0
  291. package/.worktrees/8d2d0392/src/stages/doctor.js +540 -0
  292. package/.worktrees/8d2d0392/src/stages/execute.js +1277 -0
  293. package/.worktrees/8d2d0392/src/stages/explore.js +43 -0
  294. package/.worktrees/8d2d0392/src/stages/index.js +29 -0
  295. package/.worktrees/8d2d0392/src/stages/knowledge.js +514 -0
  296. package/.worktrees/8d2d0392/src/stages/plan-postcheck.js +1398 -0
  297. package/.worktrees/8d2d0392/src/stages/plan.js +651 -0
  298. package/.worktrees/8d2d0392/src/stages/quick.js +167 -0
  299. package/.worktrees/8d2d0392/src/stages/scan.js +608 -0
  300. package/.worktrees/8d2d0392/src/stages/status.js +64 -0
  301. package/.worktrees/8d2d0392/src/stages/verify.js +246 -0
  302. package/.worktrees/8d2d0392/src/sync.js +1470 -0
  303. package/.worktrees/8d2d0392/src/task-review.js +1498 -0
  304. package/.worktrees/8d2d0392/src/taskcard-placeholders.js +21 -0
  305. package/.worktrees/8d2d0392/src/taskcard.js +217 -0
  306. package/.worktrees/8d2d0392/src/verify-postcheck.js +1718 -0
  307. package/.worktrees/8d2d0392/src/verify-probes.js +327 -0
  308. package/.worktrees/8d2d0392/src/version.js +19 -0
  309. package/.worktrees/8d2d0392/src/workflow.js +600 -0
  310. package/.worktrees/8d2d0392/src/workspace.js +125 -0
  311. package/.worktrees/8d2d0392/src/worktree-apply.js +1611 -0
  312. package/.worktrees/8d2d0392/src/worktree-deps.js +576 -0
  313. package/.worktrees/8d2d0392/src/worktree.js +1838 -0
  314. package/.worktrees/8d2d0392/templates/claude-instruction.md +23 -0
  315. package/.worktrees/8d2d0392/templates/prompts/taskcard-rules.md +29 -0
  316. package/.worktrees/8d2d0392/templates/prompts/testcase-design.md +12 -0
  317. package/.worktrees/8d2d0392/templates/prompts/verify-probes.md +32 -0
  318. package/.worktrees/8d2d0392/templates/workflows/archive-impact.yaml +79 -0
  319. package/.worktrees/8d2d0392/templates/workflows/scan-docs.yaml +132 -0
  320. package/architecture-analysis.md +448 -0
  321. package/eval/README.md +86 -0
  322. package/eval/eval.config.json +28 -0
  323. package/eval/lib/adapters/cli.mjs +63 -0
  324. package/eval/lib/adapters/dryrun.mjs +27 -0
  325. package/eval/lib/config.mjs +82 -0
  326. package/eval/lib/db.mjs +76 -0
  327. package/eval/lib/report.mjs +120 -0
  328. package/eval/lib/runner.mjs +157 -0
  329. package/eval/lib/tasks.mjs +73 -0
  330. package/eval/lib/util.mjs +107 -0
  331. package/eval/lib/workdir.mjs +30 -0
  332. package/eval/run.mjs +83 -0
  333. package/eval/tasks/001-slugify/files/package.json +6 -0
  334. package/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
  335. package/eval/tasks/001-slugify/task.json +6 -0
  336. package/eval/tasks/001-slugify/verify.mjs +28 -0
  337. package/eval/tasks/002-each-day/files/package.json +6 -0
  338. package/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
  339. package/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
  340. package/eval/tasks/002-each-day/task.json +6 -0
  341. package/eval/tasks/002-each-day/verify.mjs +31 -0
  342. package/eval/tasks/003-word-report/files/data/notes.txt +5 -0
  343. package/eval/tasks/003-word-report/files/package.json +6 -0
  344. package/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
  345. package/eval/tasks/003-word-report/task.json +6 -0
  346. package/eval/tasks/003-word-report/verify.mjs +38 -0
  347. package/package.json +4 -2
  348. package/src/agent-session-log.js +45 -5
  349. package/src/change-risk-profile.js +83 -14
  350. package/src/config-schema.js +18 -3
  351. package/src/contract-matrix.js +90 -29
  352. package/src/datetime.js +20 -0
  353. package/src/decision-distill.js +517 -0
  354. package/src/dispatch/backends/local-agent.js +4 -4
  355. package/src/docs-check.js +227 -0
  356. package/src/docs-debt.js +187 -24
  357. package/src/doctor-diagnostics.js +102 -2
  358. package/src/endpoint-extractor.js +41 -52
  359. package/src/foreign-declared.js +155 -0
  360. package/src/index.js +95 -13
  361. package/src/init.js +9 -7
  362. package/src/knowledge-match.js +107 -6
  363. package/src/local-register.js +7 -1
  364. package/src/module-resolve.js +12 -1
  365. package/src/modules.js +2 -1
  366. package/src/plan-adopt-waves.js +126 -0
  367. package/src/quicklog.js +50 -4
  368. package/src/run/command.js +124 -44
  369. package/src/run/complete-handlers.js +48 -19
  370. package/src/run/complete.js +5 -2
  371. package/src/run/gates.js +104 -22
  372. package/src/run/prompt.js +84 -42
  373. package/src/run/quick-audit.js +11 -0
  374. package/src/run/shared.js +152 -10
  375. package/src/run/stage.js +8 -5
  376. package/src/run.js +2 -2
  377. package/src/scan-postcheck.js +30 -17
  378. package/src/stage-contract-spec.js +3 -2
  379. package/src/stage-contract.js +68 -4
  380. package/src/stages/archive.js +53 -12
  381. package/src/stages/brainstorm-auto.js +50 -1
  382. package/src/stages/brainstorm.js +13 -0
  383. package/src/stages/doctor.js +157 -1
  384. package/src/stages/execute.js +32 -6
  385. package/src/stages/plan-postcheck.js +76 -17
  386. package/src/stages/plan.js +9 -4
  387. package/src/stages/quick.js +6 -1
  388. package/src/stages/verify.js +32 -15
  389. package/src/sync.js +41 -3
  390. package/src/task-review.js +2 -2
  391. package/src/taskcard-placeholders.js +21 -0
  392. package/src/taskcard.js +43 -1
  393. package/src/verify-postcheck.js +315 -13
  394. package/src/verify-probes.js +73 -10
  395. package/src/worktree-apply.js +569 -227
  396. package/src/worktree-deps.js +150 -1
  397. package/src/worktree.js +285 -77
  398. package/templates/prompts/taskcard-rules.md +2 -1
@@ -0,0 +1,480 @@
1
+ /**
2
+ * contract-matrix.js — API Contract Matrix 生成与注入
3
+ *
4
+ * plan 阶段:识别 task 之间的 provider/consumer 关系,生成契约矩阵
5
+ * execute 阶段:
6
+ * - 后端 task 完成后自动提取 endpoint artifact
7
+ * - 前端 task 开始时注入上游契约
8
+ * verify 阶段:读取 artifact 做 parity check
9
+ */
10
+
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'fs'
12
+ import { join, resolve, basename, relative } from 'path'
13
+ import { gitQuiet } from './git-helper.js'
14
+ import { splitOwnVsForeignDiffFiles } from './foreign-declared.js'
15
+ import {
16
+ scanBackendEndpoints,
17
+ scanFrontendApiCalls,
18
+ normalizePath,
19
+ diffApiParity,
20
+ } from './endpoint-extractor.js'
21
+ import { parseTaskContracts } from './stages/plan-postcheck.js'
22
+
23
+ // ─── 关键词检测 ─────────────────────────────────────────────────────────
24
+
25
+ const PROVIDER_KEYWORDS = /router|routes|endpoint|api|backend|controller|fastapi|flask|express|koa|spring/i
26
+ const CONSUMER_KEYWORDS = /frontend|client|service|apiFetch|request|fetch|axios|http/i
27
+
28
+ /**
29
+ * 判断一个 task 文档是 provider(产出 API)还是 consumer(消费 API)
30
+ * @param {string} taskContent - task markdown 内容
31
+ * @returns {{ isProvider: boolean, isConsumer: boolean, confidence: number }}
32
+ */
33
+ export function classifyTask(taskContent) {
34
+ const isProvider = PROVIDER_KEYWORDS.test(taskContent)
35
+ const isConsumer = CONSUMER_KEYWORDS.test(taskContent)
36
+ // 避免所有 task 都被标记(因为几乎所有 task 都含 "api")
37
+ // 加强判定:provider 要命中 router/endpoint/backend/controller + api
38
+ // consumer 要命中 frontend/client/apiFetch + api
39
+ const providerStrong = /router|endpoint|backend|controller|fastapi|flask/i.test(taskContent)
40
+ const consumerStrong = /frontend|apiFetch|axios|api.*client/i.test(taskContent)
41
+ const providerConfidence = providerStrong ? 0.8 : (isProvider ? 0.4 : 0)
42
+ const consumerConfidence = consumerStrong ? 0.8 : (isConsumer ? 0.4 : 0)
43
+ return {
44
+ isProvider: providerConfidence >= 0.4,
45
+ isConsumer: consumerConfidence >= 0.4,
46
+ confidence: Math.max(providerConfidence, consumerConfidence),
47
+ }
48
+ }
49
+
50
+ /**
51
+ * 解析 task 依赖关系,识别 provider → consumer 对(2026-08-20-task-truth-unify D-003@v1:
52
+ * 行内 depends_on 标注源迁 tasks.md,函数内 tasks.md 优先、plan.md 回退拼接)
53
+ * @param {string} planContent - plan.md 内容(任务总表依赖列兜底源)
54
+ * @param {string} changeDir - changes/<name>/ 目录
55
+ * @returns {Array<{ provider: string, consumer: string, type: string }>}
56
+ */
57
+ export function buildContractMatrix(planContent, changeDir) {
58
+ const contracts = []
59
+
60
+ // 解析 task 依赖关系(tasks.md 注册表行内标注优先;plan.md 总表列兜底——两源拼合去重)
61
+ // tasks.md 格式: - [ ] task-04: 名称 (depends_on: task-01,02);
62
+ // plan.md 总表: | task-04 | ... | 01 |
63
+ const tasksMdPath = changeDir ? join(changeDir, 'tasks.md') : null
64
+ let registryContent = ''
65
+ if (tasksMdPath && existsSync(tasksMdPath)) {
66
+ try { registryContent = readFileSync(tasksMdPath, 'utf8') } catch { registryContent = '' }
67
+ }
68
+ const taskDeps = parseTaskDependencies(registryContent + '\n' + String(planContent || ''))
69
+
70
+ // 读取各 task 文档,分类 provider/consumer
71
+ // classify consumers(taskDeps keys)+ providers(deps 值)。
72
+ // 旧实现只 classify consumers → provider task 不在 keys 里 → providerClass 恒 undefined
73
+ // → contracts 恒空 → 端点契约管线(buildConsumerInjection / extractArtifactsForChange)全失效。
74
+ const taskClasses = {}
75
+ const allTaskNames = new Set([...Object.keys(taskDeps), ...Object.values(taskDeps).flat()])
76
+ for (const taskName of allTaskNames) {
77
+ const taskFile = join(changeDir, 'tasks', `${taskName}.md`)
78
+ if (existsSync(taskFile)) {
79
+ taskClasses[taskName] = classifyTask(readFileSync(taskFile, 'utf8'))
80
+ }
81
+ }
82
+
83
+ // 识别契约对:A depends_on B,且 A 是 consumer,B 是 provider
84
+ for (const [consumer, deps] of Object.entries(taskDeps)) {
85
+ const consumerClass = taskClasses[consumer]
86
+ if (!consumerClass?.isConsumer) continue
87
+
88
+ for (const provider of deps) {
89
+ const providerClass = taskClasses[provider]
90
+ if (!providerClass?.isProvider) continue
91
+
92
+ // 避免自引用和重复
93
+ if (consumer === provider) continue
94
+ const alreadyExists = contracts.some(
95
+ c => c.provider === provider && c.consumer === consumer
96
+ )
97
+ if (alreadyExists) continue
98
+
99
+ contracts.push({
100
+ provider,
101
+ consumer,
102
+ type: 'api',
103
+ })
104
+ }
105
+ }
106
+
107
+ return contracts
108
+ }
109
+
110
+ /**
111
+ * 解析 task 依赖关系(2026-08-20-task-truth-unify D-003@v1)。
112
+ * 方式 2(depends_on 行内标注)的机器解析源随任务行迁 tasks.md——调用方 buildContractMatrix
113
+ * 读 tasks.md 传入(缺失回退 plan.md 内容,旧变更兼容);方式 1(任务总表依赖列)仍来自 plan.md。
114
+ * @param {string} registryContent - tasks.md 内容(优先)或 plan.md 内容(回退)
115
+ * @returns {Record<string, string[]>} task → depends_on list
116
+ */
117
+ function parseTaskDependencies(registryContent) {
118
+ const deps = {}
119
+
120
+ // 方式 1: 任务总表表格形式 | task-04 | ... | 01,02 |
121
+ // 列前缀用 [^|\d]*(不吃数字):旧 [^|]* 贪婪会吃掉 deps 列前导 0(" 01" → g2=1 → task-1),
122
+ // 导致 provider task-1.md 不存在 → contracts 恒空,端点契约管线失效。
123
+ const tableRows = registryContent.matchAll(/\|[^|]*task-(\d+)[^|]*\|[^|]*\|[^|\d]*(?:task-)?(\d+(?:\s*[,,]\s*\d+)*)[^|]*\|/gi)
124
+ for (const match of tableRows) {
125
+ const task = `task-${match[1]}`
126
+ const depList = match[2].split(/[,,\s]+/).map(d => `task-${d.trim()}`).filter(d => d.startsWith('task-'))
127
+ deps[task] = depList
128
+ }
129
+
130
+ // 方式 2: tasks.md 行内标注 "- [ ] task-04: 名称 (depends_on: task-01,02)"(新家,D-003@v1;
131
+ // 旧 plan.md checkbox 行内标注同格式兼容)
132
+ const dependsPattern = registryContent.matchAll(/task-(\d+).*?depends_on.*?(\d+(?:\s*[,,]\s*\d+)*)/gi)
133
+ for (const match of dependsPattern) {
134
+ const task = `task-${match[1]}`
135
+ const depList = match[2].split(/[,,\s]+/).map(d => `task-${d.trim()}`).filter(d => d.startsWith('task-'))
136
+ if (!deps[task]) deps[task] = []
137
+ for (const d of depList) {
138
+ if (!deps[task].includes(d)) deps[task].push(d)
139
+ }
140
+ }
141
+
142
+ return deps
143
+ }
144
+
145
+ // ─── Execute 阶段:后端 task 完成后提取 artifact ───────────────────────
146
+
147
+ /**
148
+ * 后端 task 完成后,扫描变更文件提取 endpoint artifact
149
+ * @param {string} changeDir - changes/<name>/ 目录
150
+ * @param {string} worktreePath - worktree 路径(扫描源码用)
151
+ * @param {string} specBase - .sillyspec 目录
152
+ * @param {string} taskName - task-04
153
+ * @returns {{ ok: boolean, endpoints: Array, artifactPath: string|null }}
154
+ */
155
+ export function extractProviderArtifact(changeDir, worktreePath, specBase, taskName, runtimeRoot) {
156
+ // 平台模式 contract-artifacts 落 runtimeRoot;否则落 specBase/.runtime
157
+ const artifactRoot = runtimeRoot || join(specBase, '.runtime')
158
+ // 跨变更隔离:contract-artifacts/<changeName>/<taskName>/,避免不同变更同名 task 互相覆盖污染对账
159
+ const changeName = changeDir ? basename(changeDir) : '_unknown'
160
+ const artifactDir = join(artifactRoot, 'contract-artifacts', changeName, taskName)
161
+ const artifactPath = join(artifactDir, 'endpoints.json')
162
+
163
+ if (!worktreePath || !existsSync(worktreePath)) {
164
+ return { ok: false, endpoints: [], artifactPath: null, error: 'worktree not found' }
165
+ }
166
+
167
+ try {
168
+ const endpoints = scanBackendEndpoints(worktreePath)
169
+
170
+ if (endpoints.length > 0) {
171
+ mkdirSync(artifactDir, { recursive: true })
172
+ const artifact = {
173
+ task: taskName,
174
+ type: 'backend_endpoints',
175
+ extractedAt: new Date().toISOString(),
176
+ endpoints: endpoints.map(e => ({
177
+ method: e.method,
178
+ path: normalizePath(e.path),
179
+ source: relative(worktreePath, e.source),
180
+ line: e.line,
181
+ })),
182
+ }
183
+ writeFileSync(artifactPath, JSON.stringify(artifact, null, 2) + '\n')
184
+ return { ok: true, endpoints: artifact.endpoints, artifactPath }
185
+ }
186
+
187
+ // 无端点提取到 — 不算错误(可能不是 router task)
188
+ return { ok: true, endpoints: [], artifactPath: null }
189
+ } catch (e) {
190
+ return { ok: false, endpoints: [], artifactPath: null, error: e.message }
191
+ }
192
+ }
193
+
194
+ /**
195
+ * 汇总提取一个变更内所有 provider task 的 endpoint artifact。
196
+ * run.js completeStep 在 execute Wave 完成时调用:扫 worktree → 按 buildContractMatrix 识别的
197
+ * provider task,各自落 contract-artifacts/<changeName>/<taskName>/endpoints.json。
198
+ * @param {{ changeDir: string, specBase: string, changeName: string, worktreePath: string|null }} args
199
+ * @returns {string|null} 日志摘要;无 provider/无 plan/worktree 缺失时返回 null(不打扰)
200
+ */
201
+ export function extractArtifactsForChange({ changeDir, specBase, changeName, worktreePath }) {
202
+ if (!worktreePath || !changeDir || !existsSync(changeDir)) return null
203
+ const planFile = join(changeDir, 'plan.md')
204
+ if (!existsSync(planFile)) return null
205
+ const contracts = buildContractMatrix(readFileSync(planFile, 'utf8'), changeDir)
206
+ const providers = [...new Set(contracts.map(c => c.provider))]
207
+ if (providers.length === 0) return null
208
+ let withEndpoints = 0
209
+ for (const taskName of providers) {
210
+ const r = extractProviderArtifact(changeDir, worktreePath, specBase, taskName)
211
+ if (r.ok && r.endpoints.length > 0) withEndpoints++
212
+ }
213
+ return `📦 契约 artifact 提取: providers=${providers.join(',')}(${withEndpoints}/${providers.length} 含端点)`
214
+ }
215
+
216
+ // ─── Execute 阶段:前端 task 开始时注入契约 ─────────────────────────────
217
+
218
+ /**
219
+ * 为 consumer task 构建上游契约注入文本
220
+ * @param {string} changeDir - changes/<name>/ 目录
221
+ * @param {string} specBase - .sillyspec 目录
222
+ * @param {string} taskName - 当前 task(consumer)
223
+ * @param {Array<{ provider: string, consumer: string, type: string }>} contracts
224
+ * @returns {string|null} 注入到 prompt 的契约文本,无契约时返回 null
225
+ */
226
+ export function buildConsumerInjection(changeDir, specBase, taskName, contracts, runtimeRoot) {
227
+ // 平台模式 contract-artifacts 落 runtimeRoot;否则落 specBase/.runtime
228
+ const artifactRoot = runtimeRoot || join(specBase, '.runtime')
229
+ const changeName = changeDir ? basename(changeDir) : '_unknown'
230
+ const myContracts = contracts.filter(c => c.consumer === taskName)
231
+ if (myContracts.length === 0) return null
232
+
233
+ const parts = []
234
+ for (const contract of myContracts) {
235
+ const artifactDir = join(artifactRoot, 'contract-artifacts', changeName, contract.provider)
236
+ const artifactFile = join(artifactDir, 'endpoints.json')
237
+
238
+ let endpoints = []
239
+ if (existsSync(artifactFile)) {
240
+ try {
241
+ const artifact = JSON.parse(readFileSync(artifactFile, 'utf8'))
242
+ endpoints = artifact.endpoints || []
243
+ } catch {}
244
+ }
245
+
246
+ parts.push(`### Upstream Contract: ${contract.provider}`)
247
+ if (endpoints.length > 0) {
248
+ parts.push(`\nAvailable endpoints from **${contract.provider}**:`)
249
+ for (const ep of endpoints) {
250
+ parts.push(`- **${ep.method}** \`${ep.path}\``)
251
+ }
252
+ } else {
253
+ parts.push(`\n⚠️ No endpoint artifact found for ${contract.provider}. This may indicate a contract gap.`)
254
+ }
255
+ }
256
+
257
+ if (parts.length === 0) return null
258
+
259
+ parts.unshift('## Upstream API Contracts')
260
+ parts.push('')
261
+ parts.push('### Rules')
262
+ parts.push('1. Do not invent API paths. Use only endpoints listed above.')
263
+ parts.push('2. If a required endpoint is missing, **stop and report the contract gap** instead of coding around it.')
264
+ parts.push('3. If you need to add new endpoints, you must also update the backend provider task.')
265
+
266
+ return parts.join('\n')
267
+ }
268
+
269
+ /**
270
+ * 为 consumer task 构建字段级契约注入:对比 expects_from.needs vs provider.provides.fields
271
+ *
272
+ * 让 consumer 子代理带着明确的字段清单核验上游产出:
273
+ * - provider 已承诺 → 编码时只使用 provides.fields,运行时缺字段上报
274
+ * - provider 未承诺某 needs 字段 → 标 CONTRACT_GAP,要求 stop and report(禁止 fallback 编造)
275
+ *
276
+ * 命中场景:provider task 漏实现某字段(如 DaemonRuntimeRead 缺 daemon_instance_id),
277
+ * consumer 若 fallback 编造 → 运行时 403/500。此处把"缺字段"暴露在子代理启动前。
278
+ *
279
+ * 注:plan-postcheck 已对账 expects_from↔provides,此处是 execute 时的二次保险,
280
+ * 拦截 plan-postcheck 之后 task 文件被手改、或 provider 实际实现漏字段的情况。
281
+ *
282
+ * @param {string} changeDir - changes/<name>/ 目录
283
+ * @param {string} taskName - consumer task(如 task-11)
284
+ * @returns {string|null} 注入文本,无 expects_from 时返回 null
285
+ */
286
+ export function buildContractFieldInjection(changeDir, taskName) {
287
+ const consumerFile = join(changeDir, 'tasks', `${taskName}.md`)
288
+ if (!existsSync(consumerFile)) return null
289
+ const { expectsFrom } = parseTaskContracts(readFileSync(consumerFile, 'utf8'))
290
+ const providers = Object.keys(expectsFrom)
291
+ if (providers.length === 0) return null
292
+
293
+ const lines = []
294
+ lines.push('## Upstream Contract Fields(字段级核验)')
295
+ lines.push('')
296
+
297
+ for (const providerTask of providers) {
298
+ const providerFile = join(changeDir, 'tasks', `${providerTask}.md`)
299
+ let providerProvides = []
300
+ if (existsSync(providerFile)) {
301
+ providerProvides = parseTaskContracts(readFileSync(providerFile, 'utf8')).provides
302
+ }
303
+
304
+ for (const c of expectsFrom[providerTask]) {
305
+ const providerEntry = providerProvides.find(p => p.contract === c.contract)
306
+ if (!providerEntry) {
307
+ lines.push(`### ⚠️ CONTRACT_GAP: ${providerTask} → ${c.contract}`)
308
+ lines.push(`你需要字段 [${c.needs.join(', ')}],但 ${providerTask} 的 provides 未声明此契约。`)
309
+ lines.push(`**立即停止编码并上报**:不要 fallback、不要编造字段,先确认 ${providerTask} 是否应产出此契约。`)
310
+ } else {
311
+ const providerFields = new Set(providerEntry.fields)
312
+ const missing = c.needs.filter(f => !providerFields.has(f))
313
+ if (missing.length > 0) {
314
+ lines.push(`### ⚠️ CONTRACT_GAP: ${providerTask} → ${c.contract}`)
315
+ lines.push(`你需要字段 [${missing.join(', ')}],但 ${providerTask}.provides 仅承诺 [${providerEntry.fields.join(', ')}]。`)
316
+ lines.push(`**立即停止编码并上报**:不要 fallback、不要编造字段,先要求 ${providerTask} 在 provides.fields 补上 [${missing.join(', ')}]。`)
317
+ } else {
318
+ lines.push(`### ✅ ${providerTask} → ${c.contract}`)
319
+ lines.push(`你需要的字段 [${c.needs.join(', ')}] 均在 ${providerTask}.provides 承诺内:[${providerEntry.fields.join(', ')}]。`)
320
+ lines.push(`编码时**只使用上述字段**;若运行时实际返回缺字段,说明 provider 实现漏了 → 上报 CONTRACT_GAP,不要 fallback。`)
321
+ }
322
+ }
323
+ lines.push('')
324
+ }
325
+ }
326
+
327
+ lines.push('### 字段级铁律')
328
+ lines.push('1. 禁止 fallback 编造:若上游返回缺字段,停止并上报 CONTRACT_GAP,不要用 `x || defaultValue` 之类的防御性回退掩盖契约破裂。')
329
+ lines.push('2. 只消费 provides 承诺的字段;需要新字段必须先让 provider 更新 provides。')
330
+ lines.push('3. 启动子代理前,先读 provider task 的 review.json / acceptance,确认其已声明完成上述契约字段。')
331
+
332
+ return lines.join('\n')
333
+ }
334
+
335
+ // ─── Verify 阶段:parity check ──────────────────────────────────────────
336
+
337
+ /**
338
+ * 取本变更 diff 文件集(parity 前端调用收窄用,坑 probe5-fullrepo-frontend-noise)。
339
+ * 优先 worktree meta 锚点 diff(与 verify 同源);无 meta 退主仓 git diff HEAD。
340
+ * @returns {string[]|null}
341
+ */
342
+ function _resolveDiffFilesForParity(scanRoot, changeName) {
343
+ try {
344
+ const metaPath = join(scanRoot, '.sillyspec', '.runtime', 'worktrees', changeName, 'meta.json')
345
+ if (existsSync(metaPath)) {
346
+ const meta = JSON.parse(readFileSync(metaPath, 'utf8'))
347
+ const diffBase = meta.baselineCommit || meta.actualBaseHash || meta.baseHash
348
+ const gitDir = (meta.worktreePath && meta.mode !== 'in-place-fallback' && existsSync(meta.worktreePath))
349
+ ? meta.worktreePath : scanRoot
350
+ if (diffBase) {
351
+ const out = gitQuiet(gitDir, ['diff', '--name-only', `${diffBase}..HEAD`], { timeout: 30000 })
352
+ if (out !== null) {
353
+ const files = out.split('\n').filter(Boolean)
354
+ // 并入 worktree 未提交(子代理默认不 commit 的形态,与 resolveVerifyChangedFiles 同口径)
355
+ try {
356
+ const wtStatus = gitQuiet(gitDir, ['status', '--porcelain'], { timeout: 30000, trim: false })
357
+ for (const line of String(wtStatus || '').split('\n')) {
358
+ if (!line || line.length < 4) continue
359
+ const p = line.slice(3).trim().split(' -> ').pop() || ''
360
+ const clean = p.replace(/^"|"$/g, '')
361
+ if (clean) files.push(clean)
362
+ }
363
+ } catch {}
364
+ return [...new Set(files)]
365
+ }
366
+ }
367
+ }
368
+ // 无 meta(brownfield/已 cleanup):主仓未提交 diff。他者声明归属过滤(坑
369
+ // verify-reconcile-foreign-wip):并行会话在途 WIP 的前端文件混入会产出「他者调用 ×
370
+ // 本变更端点」的 missingBackend 误报——剔除他者显式声明文件(fail-closed:无主保留)。
371
+ const out = gitQuiet(scanRoot, ['diff', '--name-only', 'HEAD'], { timeout: 30000 })
372
+ if (out === null) return null
373
+ const { own, foreign } = splitOwnVsForeignDiffFiles(scanRoot, changeName, out.split('\n').filter(Boolean))
374
+ if (foreign.length > 0) {
375
+ console.warn(`⚠️ parity 对账已排除 ${foreign.length} 个并行会话声明的文件(${foreign.slice(0, 5).map(x => x.file).join(', ')}${foreign.length > 5 ? ' 等' : ''})`)
376
+ }
377
+ return own
378
+ } catch { return null }
379
+ }
380
+
381
+ /**
382
+ * verify 阶段执行 API parity check
383
+ * @param {string} specBase - .sillyspec 目录
384
+ * @param {string} worktreePath - worktree 路径
385
+ * @returns {{ ok: boolean, missingBackend: Array, unusedBackend: Array, summary: string, backendCount: number, frontendCount: number }}
386
+ */
387
+ export function verifyApiParity(specBase, scanRoot, runtimeRoot, changeName = null) {
388
+ // 平台模式 contract-artifacts 落 runtimeRoot;否则落 specBase/.runtime
389
+ const artifactRoot = runtimeRoot || join(specBase, '.runtime')
390
+ // 读取该变更的所有 provider artifacts(contract-artifacts/<changeName>/*/endpoints.json)。
391
+ // changeName 缺失时回退扫顶层 contract-artifacts(CLI contractScan 跨变更场景兼容)。
392
+ const artifactBase = changeName
393
+ ? join(artifactRoot, 'contract-artifacts', changeName)
394
+ : join(artifactRoot, 'contract-artifacts')
395
+ const allProviderEndpoints = []
396
+
397
+ if (existsSync(artifactBase)) {
398
+ for (const entry of readdirSync(artifactBase, { withFileTypes: true })) {
399
+ if (!entry.isDirectory()) continue
400
+ const epFile = join(artifactBase, entry.name, 'endpoints.json')
401
+ if (existsSync(epFile)) {
402
+ try {
403
+ const artifact = JSON.parse(readFileSync(epFile, 'utf8'))
404
+ for (const ep of (artifact.endpoints || [])) {
405
+ allProviderEndpoints.push({
406
+ method: ep.method,
407
+ path: ep.path,
408
+ source: `${entry.name}/${ep.source}`,
409
+ })
410
+ }
411
+ } catch {}
412
+ }
413
+ }
414
+ }
415
+
416
+ // 扫描前端调用(scanRoot 通常 = 主工作区 cwd:verify 时代码已 apply 到主工作区)
417
+ if (!scanRoot || !existsSync(scanRoot)) {
418
+ return { ok: true, missingBackend: [], unusedBackend: [], summary: 'No scan root for parity check', backendCount: allProviderEndpoints.length, frontendCount: 0 }
419
+ }
420
+
421
+ // ── 现算端点并入基线(坑 probe5-endpoint-baseline-stale,2026-08-22 实证:endpoints 基线
422
+ // 是 execute 时落的存量 artifacts,verify 时主仓已被并行会话/部署扰动推进——存量基线与当前
423
+ // 代码失配 → 前端调用(当前代码)对不上旧端点集 → missingBackend 全量误报。现算当前代码的
424
+ // 后端端点并入比对集:missingBackend 判定以「当前真实代码 ∪ 存量基线」为准(现算覆盖大部分、
425
+ // 存量补充跨仓/已删代码);unusedBackend 只对现算端点算(存量过期端点不再误报 unused)。
426
+ let liveEndpoints = []
427
+ try { liveEndpoints = scanBackendEndpoints(scanRoot) } catch { /* 现算失败退回纯存量基线 */ }
428
+ const liveKeys = new Set(liveEndpoints.map(e => `${e.method} ${e.path}`))
429
+ const mergedProviderEndpoints = [...allProviderEndpoints]
430
+ for (const e of liveEndpoints) {
431
+ const key = `${e.method} ${e.path}`
432
+ if (!allProviderEndpoints.some(p => `${p.method} ${p.path}` === key)) {
433
+ mergedProviderEndpoints.push(e)
434
+ }
435
+ }
436
+
437
+ // ── 前端调用收窄到本变更 diff(坑 probe5-fullrepo-frontend-noise,2026-08-23 实证:
438
+ // 「全仓前端调用 × 本变更局部登记」口径下,未受本变更影响的存量调用对不上局部端点集
439
+ // → 143 个 missing 全是误报噪音)。比对口径 = 本变更 diff 文件内的调用 ∪ 全仓(仅当
440
+ // 端点登记源也是全仓时——无 changeName 的 CLI contractScan 场景)。diff 不可得时退回
441
+ // 全仓(不静默跳过对账)。
442
+ let frontendCalls = []
443
+ let frontendScope = 'full-repo'
444
+ if (changeName) {
445
+ try {
446
+ const changed = _resolveDiffFilesForParity(scanRoot, changeName)
447
+ if (changed && changed.length > 0) {
448
+ const changedSet = new Set(changed.map(f => f.replace(/\\/g, '/')))
449
+ frontendCalls = scanFrontendApiCalls(scanRoot).filter(c => {
450
+ const src = String(c.source || '').replace(/\\/g, '/')
451
+ return changedSet.has(src) || [...changedSet].some(cf => src.endsWith(cf) || cf.endsWith(src))
452
+ })
453
+ frontendScope = `change-diff (${changed.length} files)`
454
+ } else {
455
+ frontendCalls = scanFrontendApiCalls(scanRoot)
456
+ }
457
+ } catch { frontendCalls = scanFrontendApiCalls(scanRoot) }
458
+ } else {
459
+ frontendCalls = scanFrontendApiCalls(scanRoot)
460
+ }
461
+
462
+ const { missingBackend, unusedBackend } = diffApiParity(frontendCalls, mergedProviderEndpoints)
463
+ // unusedBackend 收窄到现算端点(存量 artifact 的过期端点不参与——它不在当前代码里,
464
+ // 「前端未调用」可能是端点已被重构掉而非真泄漏)
465
+ const narrowedUnused = unusedBackend.filter(u => {
466
+ const key = `${u.method} ${u.path}`
467
+ return liveKeys.has(key) || liveEndpoints.length === 0
468
+ })
469
+
470
+ const ok = missingBackend.length === 0
471
+ let summary = ok
472
+ ? `✅ API parity check passed: ${mergedProviderEndpoints.length} backend endpoints (live ${liveEndpoints.length} + artifact ${allProviderEndpoints.length}), ${frontendCalls.length} frontend calls [scope: ${frontendScope}]`
473
+ : `❌ API parity check failed: ${missingBackend.length} frontend calls have no matching backend endpoint [scope: ${frontendScope}]`
474
+
475
+ if (narrowedUnused.length > 0) {
476
+ summary += ` | ${narrowedUnused.length} backend endpoints unused by frontend`
477
+ }
478
+
479
+ return { ok, missingBackend, unusedBackend: narrowedUnused, summary, backendCount: mergedProviderEndpoints.length, frontendCount: frontendCalls.length }
480
+ }
@@ -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
+ }
@@ -0,0 +1,55 @@
1
+ // db-engine.js — node:sqlite DatabaseSync 引擎抽象层
2
+ // 封装 node:sqlite 内置模块 + 消解 better-sqlite3 → node:sqlite 三缺口:
3
+ // pragma → exec / transaction → 手写 SAVEPOINT / pluck → Object.values 取首列
4
+ // 供 db.js 与 doctor-diagnostics 共用(方案 B 单一换引擎点,D-002)。
5
+ // node:sqlite 在 node v22.13.0+ 无需 --experimental-sqlite flag(仍发 ExperimentalWarning)。
6
+ // Author: sillyspec execute W1 task-02
7
+ // Created: 2026-08-11
8
+ import { DatabaseSync } from 'node:sqlite';
9
+
10
+ // 打开数据库。opts.readOnly(驼峰)透传 node:sqlite。
11
+ // fileMustExist 语义由调用方 existsSync 前置门实现(node:sqlite 不拒缺失文件,不凭空建库由调用方保证)。
12
+ export function openDatabase(dbPath, opts = {}) {
13
+ return new DatabaseSync(dbPath, { readOnly: !!opts.readOnly });
14
+ }
15
+
16
+ // 逐条 exec PRAGMA。entries: [['journal_mode','WAL'], ['busy_timeout','5000'], ...]
17
+ // node:sqlite 无 .pragma(),统一走 .exec('PRAGMA key = value')。
18
+ export function applyPragmas(db, entries) {
19
+ for (const [key, value] of entries) {
20
+ db.exec(`PRAGMA ${key} = ${value}`);
21
+ }
22
+ }
23
+
24
+ // 手写事务(node:sqlite 无 .transaction())。
25
+ // 统一用 SAVEPOINT/RELEASE/ROLLBACK TO —— SQLite 允许顶层 SAVEPOINT(等价事务),
26
+ // 嵌套调用自动形成 savepoint 栈(better-sqlite3 嵌套自动 SAVEPOINT 的兼容实现)。
27
+ // fn 抛错自动 ROLLBACK TO + RELEASE 且原错误上抛不吞。
28
+ // 本函数不含 BUSY 重试(由 db.js wrapper 外层包裹)。
29
+ export function runTransaction(db, fn) {
30
+ const sp = 'tx_sp_' + Math.random().toString(36).slice(2);
31
+ db.exec(`SAVEPOINT ${sp}`);
32
+ try {
33
+ const result = fn();
34
+ db.exec(`RELEASE ${sp}`);
35
+ return result;
36
+ } catch (err) {
37
+ try {
38
+ db.exec(`ROLLBACK TO ${sp}`);
39
+ db.exec(`RELEASE ${sp}`);
40
+ } catch { /* 回滚失败不掩盖原错误 */ }
41
+ throw err;
42
+ }
43
+ }
44
+
45
+ // pluck 替代:取第一行第一列,无行 undefined。
46
+ // node:sqlite .get() 返回行对象(不像 better-sqlite3 pluck 返标量),故 Object.values 取首列。
47
+ export function pluckGet(db, sql, ...params) {
48
+ const row = db.prepare(sql).get(...params);
49
+ return row === undefined ? undefined : Object.values(row)[0];
50
+ }
51
+
52
+ // pluck 替代:取所有行第一列成数组。
53
+ export function pluckAll(db, sql, ...params) {
54
+ return db.prepare(sql).all(...params).map((row) => Object.values(row)[0]);
55
+ }