@winspan/claude-forge 8.50.6 → 8.51.1

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 (367) hide show
  1. package/CLAUDE.md +7 -7
  2. package/dist/claudemd/claudemd-generator.d.ts.map +1 -1
  3. package/dist/claudemd/claudemd-generator.js +27 -237
  4. package/dist/claudemd/claudemd-generator.js.map +1 -1
  5. package/dist/claudemd/resume-manager.js +1 -1
  6. package/dist/claudemd/resume-manager.js.map +1 -1
  7. package/dist/claudemd/templates/swarm-protocol.md +222 -0
  8. package/dist/cli/commands/daemon.js +6 -6
  9. package/dist/cli/commands/daemon.js.map +1 -1
  10. package/dist/cli/commands/executions.d.ts.map +1 -1
  11. package/dist/cli/commands/executions.js +4 -3
  12. package/dist/cli/commands/executions.js.map +1 -1
  13. package/dist/cli/commands/init.js +2 -2
  14. package/dist/cli/commands/init.js.map +1 -1
  15. package/dist/cli/commands/logs.js.map +1 -1
  16. package/dist/cli/commands/mcp.d.ts.map +1 -1
  17. package/dist/cli/commands/mcp.js +3 -5
  18. package/dist/cli/commands/mcp.js.map +1 -1
  19. package/dist/cli/commands/menu.d.ts.map +1 -1
  20. package/dist/cli/commands/menu.js +4 -3
  21. package/dist/cli/commands/menu.js.map +1 -1
  22. package/dist/cli/commands/stats.d.ts.map +1 -1
  23. package/dist/cli/commands/stats.js +2 -3
  24. package/dist/cli/commands/stats.js.map +1 -1
  25. package/dist/cli/commands/status.js +2 -2
  26. package/dist/cli/commands/status.js.map +1 -1
  27. package/dist/cli/commands/trace.d.ts.map +1 -1
  28. package/dist/cli/commands/trace.js +11 -23
  29. package/dist/cli/commands/trace.js.map +1 -1
  30. package/dist/cli/init/hook-manager.d.ts.map +1 -1
  31. package/dist/cli/init/hook-manager.js +2 -2
  32. package/dist/cli/init/hook-manager.js.map +1 -1
  33. package/dist/core/ai/provider.js +2 -2
  34. package/dist/core/ai/provider.js.map +1 -1
  35. package/dist/core/constants.d.ts +12 -1
  36. package/dist/core/constants.d.ts.map +1 -1
  37. package/dist/core/constants.js +15 -1
  38. package/dist/core/constants.js.map +1 -1
  39. package/dist/core/event-fields.d.ts +16 -0
  40. package/dist/core/event-fields.d.ts.map +1 -0
  41. package/dist/core/event-fields.js +19 -0
  42. package/dist/core/event-fields.js.map +1 -0
  43. package/dist/core/queue/index.d.ts.map +1 -1
  44. package/dist/core/queue/index.js +3 -4
  45. package/dist/core/queue/index.js.map +1 -1
  46. package/dist/core/storage/base.d.ts +36 -3
  47. package/dist/core/storage/base.d.ts.map +1 -1
  48. package/dist/core/storage/base.js +101 -58
  49. package/dist/core/storage/base.js.map +1 -1
  50. package/dist/core/storage/events.d.ts +92 -3
  51. package/dist/core/storage/events.d.ts.map +1 -1
  52. package/dist/core/storage/events.js +147 -0
  53. package/dist/core/storage/events.js.map +1 -1
  54. package/dist/core/storage/routing.d.ts +54 -1
  55. package/dist/core/storage/routing.d.ts.map +1 -1
  56. package/dist/core/storage/routing.js +99 -1
  57. package/dist/core/storage/routing.js.map +1 -1
  58. package/dist/core/storage/schema.sql +12 -2
  59. package/dist/core/storage/sessions.d.ts +20 -0
  60. package/dist/core/storage/sessions.d.ts.map +1 -1
  61. package/dist/core/storage/sessions.js +59 -0
  62. package/dist/core/storage/sessions.js.map +1 -1
  63. package/dist/core/storage/skills.d.ts +23 -0
  64. package/dist/core/storage/skills.d.ts.map +1 -1
  65. package/dist/core/storage/skills.js +47 -0
  66. package/dist/core/storage/skills.js.map +1 -1
  67. package/dist/core/storage/sqlite.d.ts +35 -2
  68. package/dist/core/storage/sqlite.d.ts.map +1 -1
  69. package/dist/core/storage/sqlite.js +93 -4
  70. package/dist/core/storage/sqlite.js.map +1 -1
  71. package/dist/core/storage/tasks.d.ts +49 -0
  72. package/dist/core/storage/tasks.d.ts.map +1 -1
  73. package/dist/core/storage/tasks.js +143 -1
  74. package/dist/core/storage/tasks.js.map +1 -1
  75. package/dist/core/storage/token-usage.d.ts +1 -1
  76. package/dist/core/storage/token-usage.d.ts.map +1 -1
  77. package/dist/core/storage/token-usage.js +1 -1
  78. package/dist/core/storage/token-usage.js.map +1 -1
  79. package/dist/core/types.d.ts +24 -3
  80. package/dist/core/types.d.ts.map +1 -1
  81. package/dist/core/types.js.map +1 -1
  82. package/dist/core/utils/error-handler.d.ts.map +1 -1
  83. package/dist/core/utils/error-handler.js +3 -2
  84. package/dist/core/utils/error-handler.js.map +1 -1
  85. package/dist/core/utils/git.d.ts +10 -0
  86. package/dist/core/utils/git.d.ts.map +1 -0
  87. package/dist/core/utils/git.js +24 -0
  88. package/dist/core/utils/git.js.map +1 -0
  89. package/dist/core/utils/logger.d.ts.map +1 -1
  90. package/dist/core/utils/logger.js +15 -1
  91. package/dist/core/utils/logger.js.map +1 -1
  92. package/dist/core/utils/lru-cache.d.ts +1 -0
  93. package/dist/core/utils/lru-cache.d.ts.map +1 -1
  94. package/dist/core/utils/lru-cache.js +3 -0
  95. package/dist/core/utils/lru-cache.js.map +1 -1
  96. package/dist/core/utils/token-tracker.js +1 -1
  97. package/dist/core/utils/token-tracker.js.map +1 -1
  98. package/dist/daemon/event-parser.d.ts.map +1 -1
  99. package/dist/daemon/event-parser.js +2 -1
  100. package/dist/daemon/event-parser.js.map +1 -1
  101. package/dist/daemon/handlers/history-exporter.js.map +1 -1
  102. package/dist/daemon/handlers/post-tool-use.d.ts.map +1 -1
  103. package/dist/daemon/handlers/post-tool-use.js +7 -3
  104. package/dist/daemon/handlers/post-tool-use.js.map +1 -1
  105. package/dist/daemon/handlers/stop.d.ts +4 -0
  106. package/dist/daemon/handlers/stop.d.ts.map +1 -1
  107. package/dist/daemon/handlers/stop.js +23 -35
  108. package/dist/daemon/handlers/stop.js.map +1 -1
  109. package/dist/daemon/handlers/user-prompt.d.ts +3 -3
  110. package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
  111. package/dist/daemon/handlers/user-prompt.js +12 -22
  112. package/dist/daemon/handlers/user-prompt.js.map +1 -1
  113. package/dist/daemon/hook-sync.d.ts +17 -0
  114. package/dist/daemon/hook-sync.d.ts.map +1 -0
  115. package/dist/daemon/hook-sync.js +74 -0
  116. package/dist/daemon/hook-sync.js.map +1 -0
  117. package/dist/daemon/index.d.ts.map +1 -1
  118. package/dist/daemon/index.js +33 -9
  119. package/dist/daemon/index.js.map +1 -1
  120. package/dist/daemon/lifecycle.js +3 -4
  121. package/dist/daemon/lifecycle.js.map +1 -1
  122. package/dist/daemon/server.d.ts +6 -4
  123. package/dist/daemon/server.d.ts.map +1 -1
  124. package/dist/daemon/server.js +76 -85
  125. package/dist/daemon/server.js.map +1 -1
  126. package/dist/daemon/services/task-segmenter.js +1 -1
  127. package/dist/daemon/services/task-segmenter.js.map +1 -1
  128. package/dist/hooks/hook-lib.sh +37 -0
  129. package/dist/hooks/notification.sh +2 -2
  130. package/dist/hooks/post-tool-use.sh +2 -2
  131. package/dist/hooks/pre-tool-use.sh +2 -2
  132. package/dist/hooks/stop.sh +9 -6
  133. package/dist/hooks/user-prompt-submit.sh +2 -2
  134. package/dist/{daemon/services → web/analytics}/anti-pattern-detector.d.ts +3 -4
  135. package/dist/web/analytics/anti-pattern-detector.d.ts.map +1 -0
  136. package/dist/{daemon/services → web/analytics}/anti-pattern-detector.js +7 -46
  137. package/dist/{daemon/services → web/analytics}/anti-pattern-detector.js.map +1 -1
  138. package/dist/web/analytics/drift-detector.d.ts.map +1 -0
  139. package/dist/{daemon/services → web/analytics}/drift-detector.js +10 -13
  140. package/dist/web/analytics/drift-detector.js.map +1 -0
  141. package/dist/web/analytics/weekly-report.d.ts.map +1 -0
  142. package/dist/{daemon/services → web/analytics}/weekly-report.js +51 -50
  143. package/dist/web/analytics/weekly-report.js.map +1 -0
  144. package/dist/web/auth-middleware.d.ts.map +1 -1
  145. package/dist/web/auth-middleware.js +1 -2
  146. package/dist/web/auth-middleware.js.map +1 -1
  147. package/dist/web/routes/_helpers.d.ts +16 -0
  148. package/dist/web/routes/_helpers.d.ts.map +1 -0
  149. package/dist/web/routes/_helpers.js +32 -0
  150. package/dist/web/routes/_helpers.js.map +1 -0
  151. package/dist/web/routes/drift.js +1 -1
  152. package/dist/web/routes/drift.js.map +1 -1
  153. package/dist/web/routes/insights.js +1 -1
  154. package/dist/web/routes/insights.js.map +1 -1
  155. package/dist/web/routes/reports.js +1 -1
  156. package/dist/web/routes/reports.js.map +1 -1
  157. package/dist/web/routes/rules.d.ts +3 -0
  158. package/dist/web/routes/rules.d.ts.map +1 -1
  159. package/dist/web/routes/rules.js +28 -52
  160. package/dist/web/routes/rules.js.map +1 -1
  161. package/dist/web/routes/sessions.d.ts.map +1 -1
  162. package/dist/web/routes/sessions.js +16 -30
  163. package/dist/web/routes/sessions.js.map +1 -1
  164. package/dist/web/routes/skill-stats.d.ts +2 -0
  165. package/dist/web/routes/skill-stats.d.ts.map +1 -1
  166. package/dist/web/routes/skill-stats.js +28 -64
  167. package/dist/web/routes/skill-stats.js.map +1 -1
  168. package/dist/web/routes/skills.d.ts.map +1 -1
  169. package/dist/web/routes/skills.js +5 -4
  170. package/dist/web/routes/skills.js.map +1 -1
  171. package/dist/web/routes/stats.d.ts +4 -0
  172. package/dist/web/routes/stats.d.ts.map +1 -1
  173. package/dist/web/routes/stats.js +19 -21
  174. package/dist/web/routes/stats.js.map +1 -1
  175. package/dist/web/routes/tasks.d.ts.map +1 -1
  176. package/dist/web/routes/tasks.js +17 -42
  177. package/dist/web/routes/tasks.js.map +1 -1
  178. package/dist/web/routes/trace.d.ts.map +1 -1
  179. package/dist/web/routes/trace.js +7 -17
  180. package/dist/web/routes/trace.js.map +1 -1
  181. package/dist/web/routes/types.d.ts.map +1 -1
  182. package/dist/web/routes/types.js +4 -3
  183. package/dist/web/routes/types.js.map +1 -1
  184. package/dist/web/static/assets/{AIConfig-BQCAQE9D.js → AIConfig-CdDWzJyO.js} +2 -2
  185. package/dist/web/static/assets/{AIConfig-BQCAQE9D.js.map → AIConfig-CdDWzJyO.js.map} +1 -1
  186. package/dist/web/static/assets/{Dashboard-D7Bo6Kan.js → Dashboard-CoEmmIDt.js} +2 -2
  187. package/dist/web/static/assets/{Dashboard-D7Bo6Kan.js.map → Dashboard-CoEmmIDt.js.map} +1 -1
  188. package/dist/web/static/assets/{Drawer-BeHRQxUS.js → Drawer-DdRTzlLB.js} +2 -2
  189. package/dist/web/static/assets/{Drawer-BeHRQxUS.js.map → Drawer-DdRTzlLB.js.map} +1 -1
  190. package/dist/web/static/assets/{Events-K_tCY2ti.js → Events-DrIq1SUS.js} +2 -2
  191. package/dist/web/static/assets/{Events-K_tCY2ti.js.map → Events-DrIq1SUS.js.map} +1 -1
  192. package/dist/web/static/assets/{Reports-BJCmBnc_.js → Reports-DFBM3MDK.js} +2 -2
  193. package/dist/web/static/assets/{Reports-BJCmBnc_.js.map → Reports-DFBM3MDK.js.map} +1 -1
  194. package/dist/web/static/assets/{SearchInput-BX2KhMkw.js → SearchInput-qCj_jAcf.js} +2 -2
  195. package/dist/web/static/assets/{SearchInput-BX2KhMkw.js.map → SearchInput-qCj_jAcf.js.map} +1 -1
  196. package/dist/web/static/assets/{SessionDetail-Bkr-kC7V.js → SessionDetail-CCzwdoT7.js} +2 -2
  197. package/dist/web/static/assets/{SessionDetail-Bkr-kC7V.js.map → SessionDetail-CCzwdoT7.js.map} +1 -1
  198. package/dist/web/static/assets/{Sessions-Chx9OCLH.js → Sessions-FfLYkAw9.js} +2 -2
  199. package/dist/web/static/assets/{Sessions-Chx9OCLH.js.map → Sessions-FfLYkAw9.js.map} +1 -1
  200. package/dist/web/static/assets/{Skills-O0GT1i7m.js → Skills-C8Gvs3Qa.js} +2 -2
  201. package/dist/web/static/assets/{Skills-O0GT1i7m.js.map → Skills-C8Gvs3Qa.js.map} +1 -1
  202. package/dist/web/static/assets/TaskDetail-BS8pYhaR.js +2 -0
  203. package/dist/web/static/assets/TaskDetail-BS8pYhaR.js.map +1 -0
  204. package/dist/web/static/assets/Tasks-CyuhizG8.js +2 -0
  205. package/dist/web/static/assets/Tasks-CyuhizG8.js.map +1 -0
  206. package/dist/web/static/assets/index-CBX47X8l.js +3 -0
  207. package/dist/web/static/assets/{index-DxIbmNmr.js.map → index-CBX47X8l.js.map} +1 -1
  208. package/dist/web/static/assets/index-DjIoMdoR.css +1 -0
  209. package/dist/web/static/assets/{lucide-fJlPI3H7.js → lucide-Bs_edTLa.js} +44 -39
  210. package/dist/web/static/assets/lucide-Bs_edTLa.js.map +1 -0
  211. package/dist/web/static/assets/react-router-r79dBVy4.js +20 -0
  212. package/dist/web/static/assets/{react-router-I-HqunH7.js.map → react-router-r79dBVy4.js.map} +1 -1
  213. package/dist/web/static/assets/task-title-BhOcemuR.js +2 -0
  214. package/dist/web/static/assets/task-title-BhOcemuR.js.map +1 -0
  215. package/dist/web/static/index.html +4 -4
  216. package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +299 -0
  217. package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +191 -0
  218. package/docs/design/h3-fallback-removal-spec-20260518-1245.md +76 -0
  219. package/docs/design/h4-index-dedup-spec-20260518-1230.md +109 -0
  220. package/docs/design/h6-services-migration-spec-20260518-1355.md +82 -0
  221. package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +106 -0
  222. package/docs/design/m10-forge-paths-spec-20260518-1320.md +121 -0
  223. package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +131 -0
  224. package/docs/design/m7-routing-event-association-spec-20260518-1545.md +103 -0
  225. package/docs/design/project-path-gitroot-spec-20260518-1715.md +134 -0
  226. package/docs/design/task-active-gc-spec-20260518-1745.md +146 -0
  227. package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +82 -0
  228. package/docs/implementation/h2-final-changelog-20260518-1530.md +61 -0
  229. package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +70 -0
  230. package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +120 -0
  231. package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +71 -0
  232. package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +71 -0
  233. package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +60 -0
  234. package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +46 -0
  235. package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +46 -0
  236. package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +45 -0
  237. package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +63 -0
  238. package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +38 -0
  239. package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +58 -0
  240. package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +60 -0
  241. package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +43 -0
  242. package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +56 -0
  243. package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +69 -0
  244. package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +63 -0
  245. package/docs/implementation/task-active-gc-changelog-20260518-1745.md +35 -0
  246. package/docs/implementation/task-title-summary-changelog-20260518-1130.md +39 -0
  247. package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +22 -0
  248. package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +56 -0
  249. package/docs/reviews/task-title-summary.md +92 -0
  250. package/docs/reviews/tasks-detail-back-loses-filters.md +58 -0
  251. package/docs/reviews/tasks-page-white-screen-hotfix.md +126 -0
  252. package/package.json +2 -2
  253. package/src/claudemd/claudemd-generator.ts +29 -238
  254. package/src/claudemd/resume-manager.ts +1 -1
  255. package/src/claudemd/templates/swarm-protocol.md +222 -0
  256. package/src/cli/commands/daemon.ts +6 -6
  257. package/src/cli/commands/executions.ts +4 -3
  258. package/src/cli/commands/init.ts +2 -2
  259. package/src/cli/commands/logs.ts +1 -1
  260. package/src/cli/commands/mcp.ts +3 -5
  261. package/src/cli/commands/menu.ts +4 -3
  262. package/src/cli/commands/stats.ts +2 -3
  263. package/src/cli/commands/status.ts +2 -2
  264. package/src/cli/commands/trace.ts +10 -26
  265. package/src/cli/init/hook-manager.ts +2 -2
  266. package/src/core/ai/provider.ts +2 -2
  267. package/src/core/constants.ts +18 -1
  268. package/src/core/event-fields.ts +32 -0
  269. package/src/core/queue/index.ts +3 -4
  270. package/src/core/storage/base.ts +132 -56
  271. package/src/core/storage/events.ts +183 -4
  272. package/src/core/storage/routing.ts +129 -1
  273. package/src/core/storage/schema.sql +12 -2
  274. package/src/core/storage/sessions.ts +64 -0
  275. package/src/core/storage/skills.ts +69 -0
  276. package/src/core/storage/sqlite.ts +103 -4
  277. package/src/core/storage/tasks.ts +149 -1
  278. package/src/core/storage/token-usage.ts +1 -1
  279. package/src/core/types.ts +30 -3
  280. package/src/core/utils/error-handler.ts +3 -2
  281. package/src/core/utils/git.ts +23 -0
  282. package/src/core/utils/logger.ts +16 -1
  283. package/src/core/utils/lru-cache.ts +4 -0
  284. package/src/core/utils/token-tracker.ts +1 -1
  285. package/src/daemon/event-parser.ts +4 -3
  286. package/src/daemon/handlers/history-exporter.ts +1 -1
  287. package/src/daemon/handlers/post-tool-use.ts +7 -3
  288. package/src/daemon/handlers/stop.ts +32 -39
  289. package/src/daemon/handlers/user-prompt.ts +12 -22
  290. package/src/daemon/hook-sync.ts +91 -0
  291. package/src/daemon/index.ts +34 -10
  292. package/src/daemon/lifecycle.ts +3 -3
  293. package/src/daemon/server.ts +76 -89
  294. package/src/daemon/services/task-segmenter.ts +1 -1
  295. package/src/hooks/hook-lib.sh +37 -0
  296. package/src/hooks/notification.sh +2 -2
  297. package/src/hooks/post-tool-use.sh +2 -2
  298. package/src/hooks/pre-tool-use.sh +2 -2
  299. package/src/hooks/stop.sh +9 -6
  300. package/src/hooks/user-prompt-submit.sh +2 -2
  301. package/src/{daemon/services → web/analytics}/anti-pattern-detector.ts +9 -54
  302. package/src/{daemon/services → web/analytics}/drift-detector.ts +10 -23
  303. package/src/{daemon/services → web/analytics}/weekly-report.ts +52 -75
  304. package/src/web/auth-middleware.ts +1 -2
  305. package/src/web/routes/_helpers.ts +34 -0
  306. package/src/web/routes/drift.ts +1 -1
  307. package/src/web/routes/insights.ts +1 -1
  308. package/src/web/routes/reports.ts +1 -1
  309. package/src/web/routes/rules.ts +31 -56
  310. package/src/web/routes/sessions.ts +18 -30
  311. package/src/web/routes/skill-stats.ts +29 -69
  312. package/src/web/routes/skills.ts +5 -4
  313. package/src/web/routes/stats.ts +19 -29
  314. package/src/web/routes/tasks.ts +17 -42
  315. package/src/web/routes/trace.ts +7 -19
  316. package/src/web/routes/types.ts +4 -3
  317. package/tests/integration/claudemd-generator.test.ts +90 -0
  318. package/tests/integration/web-analytics.integration.test.ts +133 -0
  319. package/tests/integration/web-stats.integration.test.ts +135 -0
  320. package/tests/integration/web-trace.integration.test.ts +175 -0
  321. package/tests/unit/core/forge-paths.test.ts +99 -0
  322. package/tests/unit/daemon/hook-sync.test.ts +71 -0
  323. package/tests/unit/daemon/post-tool-use.test.ts +121 -0
  324. package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +202 -0
  325. package/tests/unit/daemon/task-segmenter-recover.test.ts +84 -0
  326. package/tests/unit/event-fields.test.ts +88 -0
  327. package/tests/unit/event-parser.test.ts +55 -0
  328. package/tests/unit/hooks/resolve-project-path.test.ts +122 -0
  329. package/tests/unit/socket-server.test.ts +183 -0
  330. package/tests/unit/storage/event-operations-aggregates.test.ts +342 -0
  331. package/tests/unit/storage/migration-idempotent.test.ts +304 -0
  332. package/tests/unit/storage/routing-aggregates.test.ts +276 -0
  333. package/tests/unit/storage/routing.test.ts +117 -0
  334. package/tests/unit/storage/schema-missing.test.ts +81 -0
  335. package/tests/unit/storage/session-operations-aggregates.test.ts +120 -0
  336. package/tests/unit/storage/skill-operations-counts.test.ts +106 -0
  337. package/tests/unit/storage/skills-aggregates.test.ts +104 -0
  338. package/tests/unit/storage/sqlite-refactor-harness.test.ts +3 -3
  339. package/tests/unit/storage/task-operations-counts.test.ts +46 -0
  340. package/tests/unit/storage/tasks-getById.test.ts +343 -0
  341. package/tests/unit/storage/tasks-stale-gc.test.ts +86 -0
  342. package/tests/unit/token-usage.test.ts +6 -6
  343. package/tests/unit/web/navigation-back-contract.test.ts +134 -0
  344. package/tests/unit/web/routes-rules.test.ts +182 -0
  345. package/tests/unit/web/routes-tasks.test.ts +34 -0
  346. package/tests/unit/web/task-title-contract.test.ts +210 -0
  347. package/tests/unit/web/tasks-component-contract.test.ts +179 -0
  348. package/vitest.config.ts +1 -1
  349. package/web/src/pages/TaskDetail.tsx +9 -5
  350. package/web/src/pages/Tasks.tsx +315 -50
  351. package/web/src/utils/navigation.ts +25 -0
  352. package/web/src/utils/task-title.ts +49 -0
  353. package/dist/daemon/services/anti-pattern-detector.d.ts.map +0 -1
  354. package/dist/daemon/services/drift-detector.d.ts.map +0 -1
  355. package/dist/daemon/services/drift-detector.js.map +0 -1
  356. package/dist/daemon/services/weekly-report.d.ts.map +0 -1
  357. package/dist/daemon/services/weekly-report.js.map +0 -1
  358. package/dist/web/static/assets/TaskDetail-5SR8zGzv.js +0 -2
  359. package/dist/web/static/assets/TaskDetail-5SR8zGzv.js.map +0 -1
  360. package/dist/web/static/assets/Tasks-DCgDqvOZ.js +0 -2
  361. package/dist/web/static/assets/Tasks-DCgDqvOZ.js.map +0 -1
  362. package/dist/web/static/assets/index-D8AKj26b.css +0 -1
  363. package/dist/web/static/assets/index-DxIbmNmr.js +0 -3
  364. package/dist/web/static/assets/lucide-fJlPI3H7.js.map +0 -1
  365. package/dist/web/static/assets/react-router-I-HqunH7.js +0 -20
  366. /package/dist/{daemon/services → web/analytics}/drift-detector.d.ts +0 -0
  367. /package/dist/{daemon/services → web/analytics}/weekly-report.d.ts +0 -0
@@ -0,0 +1,56 @@
1
+ # M6 + M8 实施 Changelog
2
+
3
+ **Date**: 2026-05-18 13:40
4
+ **Status**: 完成
5
+
6
+ ## 完成清单
7
+
8
+ - [x] M8: daemon shutdown 加 clearInterval
9
+ - [x] M6: `recordTokenUsage` → `writeTokenUsage`
10
+ - [x] M6: `findPendingRoutingEvents` → `queryPendingRoutingEvents`
11
+ - [x] M6: `fetchEventsSince` → `queryEventsSince`(顺手 `fetchSessionsSince` → `querySessionsSince`)
12
+
13
+ ## 关键代码定位
14
+
15
+ ### M8: daemon shutdown clearInterval
16
+
17
+ - `src/daemon/index.ts:111-119` — `setInterval` 返回值保存到 `maintenanceInterval` 常量
18
+ - `src/daemon/index.ts:254` — `shutdown()` 中新增 `clearInterval(maintenanceInterval)`,置于 webServer.stop 之前、process.exit 之前
19
+
20
+ ### M6: `recordTokenUsage` → `writeTokenUsage`
21
+
22
+ - `src/core/storage/token-usage.ts:13` — `TokenUsageOperations.writeTokenUsage`(定义)
23
+ - `src/core/storage/sqlite.ts:186-188` — facade 转发更新(含 `Parameters<...>` 泛型引用同步)
24
+ - `src/core/utils/token-tracker.ts:36` — 业务调用方
25
+ - `tests/unit/token-usage.test.ts` — 6 处调用点(replace_all)
26
+ - `tests/unit/storage/sqlite-refactor-harness.test.ts:231-246` — 测试调用 + test name 同步更新
27
+
28
+ ### M6: `findPendingRoutingEvents` → `queryPendingRoutingEvents`
29
+
30
+ - `src/core/storage/routing.ts:130` — `RoutingOperations.queryPendingRoutingEvents`(定义)
31
+ - `src/core/storage/sqlite.ts:167-169` — facade 转发更新
32
+
33
+ 无外部调用方(grep 仅命中定义与 facade)。
34
+
35
+ ### M6: `fetchEventsSince` / `fetchSessionsSince` → `queryEventsSince` / `querySessionsSince`
36
+
37
+ - `src/daemon/services/anti-pattern-detector.ts:58-59` — 内部调用点
38
+ - `src/daemon/services/anti-pattern-detector.ts:82` — `private queryEventsSince`
39
+ - `src/daemon/services/anti-pattern-detector.ts:102` — `private querySessionsSince`
40
+
41
+ 均为私有方法,无类外引用。
42
+
43
+ ## 测试结果
44
+
45
+ - `npx tsc --noEmit` → **0 errors**
46
+ - `npx vitest run tests/unit/storage/ tests/unit/token-usage.test.ts --reporter=dot` → 67 passed / 1 failed(pre-existing `linkEventToTask`)
47
+ - `npm test -- --reporter=dot` → 439 passed / 1 failed(同一 pre-existing 失败)
48
+
49
+ ## 顺手发现的其它命名违规
50
+
51
+ - `fetchSessionsSince`(在 anti-pattern-detector.ts,与 `fetchEventsSince` 配对)一并改为 `querySessionsSince`。
52
+ - 未发现其它 `findXxx`/`fetchYyy`/`retrieveZzz` 违规(grep 已扫过 src/ tests/)。
53
+
54
+ ## 已知问题
55
+
56
+ - `tests/unit/storage/sqlite-refactor-harness.test.ts` 中 `linkEventToTask associates events with tasks` 用例 pre-existing 失败(writeEvent 校验抛错),与本次改名无关。
@@ -0,0 +1,69 @@
1
+ # M7 实施 Changelog
2
+
3
+ **Date**: 2026-05-18 15:45
4
+ **Spec**: docs/design/m7-routing-event-association-spec-20260518-1545.md
5
+ **Status**: 完成
6
+
7
+ ## 完成清单
8
+ - [x] routing.ts 加 getRecentPendingRoutingEvent
9
+ - [x] sqlite.ts facade 透传
10
+ - [x] storage 单测 5 case(4 必需 + 1 legacy 行为锁定)
11
+ - [x] post-tool-use.ts 切换调用
12
+ - [x] handler 单测 3 case
13
+ - [x] 文档化已知限制
14
+
15
+ ## 关键代码定位
16
+
17
+ - 新方法实现:`src/core/storage/routing.ts:148-170`
18
+ - Facade 透传:`src/core/storage/sqlite.ts:225-227`
19
+ - Handler 切换:`src/daemon/handlers/post-tool-use.ts:32-36`
20
+ - Storage 单测:`tests/unit/storage/routing.test.ts:1-112`
21
+ - Handler 单测:`tests/unit/daemon/post-tool-use.test.ts:1-119`
22
+
23
+ ## 测试结果
24
+
25
+ - **新增 storage 测试**:5/5 passed
26
+ - case 1: 旧 obeyed=1 + 新 pending → 返回新 pending
27
+ - case 2: 全部 obeyed=1 → 返回 null
28
+ - case 3: session 隔离
29
+ - case 4: 单条 pending → 返回该条
30
+ - bonus: legacy `getRecentRoutingEvent` 行为锁定(仍返回 most-recent,不受 obeyed 影响)
31
+ - **新增 handler 测试**:3/3 passed
32
+ - case 1(核心 bug 验证):同 prompt 第 2 个 Agent 不二次覆盖原 routing_event
33
+ - case 2: 跨 prompt 各自关联
34
+ - case 3: 无 routing_event 不抛错
35
+ - **tsc --noEmit**:0 errors
36
+ - **npm test 全量**:549 passed / 1 failed = 549/550
37
+ - 唯一失败 `linkEventToTask associates events with tasks`,与 M7 无关(spec 第 86 行已标注 pre-existing)
38
+
39
+ ## 实施细节
40
+
41
+ ### 向后兼容策略
42
+
43
+ - **保留** 旧 `getRecentRoutingEvent`(无 `obeyed IS NULL` 过滤)不动
44
+ - **新增** `getRecentPendingRoutingEvent`(带 `obeyed IS NULL` 过滤)
45
+ - Handler 仅切换到新方法;其他调用方(如有)维持旧语义
46
+
47
+ ### Bug 修复验证
48
+
49
+ handler 单测 case 1 直接复现了 spec 描述的"同 prompt 多 Agent 覆盖"场景:
50
+
51
+ ```ts
52
+ // Before M7: Agent#2 也会拿到 routing_event#1 → routed_to_name 被覆盖为 'coder'
53
+ // After M7: Agent#2 拿不到 pending(#1 已 obeyed=1)→ 不再触碰 #1
54
+ expect(rowsAfter2[0].routed_to_name).toBe('researcher'); // NOT 'coder'
55
+ expect(rowsAfter2[0].first_tool_ts).toBe(firstToolTs); // 不被 stomp
56
+ ```
57
+
58
+ ## 已知限制(文档化)
59
+
60
+ - **同 prompt 多 Agent 漏记**:第 2+ 个 Agent 在 M7 修复后会被 silently skip(找不到 pending)。
61
+ - 影响:该 Agent 调用在 routing_events 表中没有归属行。
62
+ - 不在 M7 范围内:要彻底解决须把 PostToolUse 改为 **INSERT** 新行(而非 UPDATE),属功能扩展。
63
+ - 与 M7 前的对比:M7 前是"覆盖原行"(数据丢失 + 错配),M7 后是"放弃记录"(数据缺失但不错配)。语义更可控。
64
+
65
+ - **旧库残留 obeyed=NULL 的 stale 行**:若 session 内存在非常老的孤儿 pending(如旧 daemon crash 留下),可能被新 prompt 的 Agent 错关联。受 `ORDER BY ts DESC + session_id` 限制,影响面极小,可接受。
66
+
67
+ ## 已知问题
68
+
69
+ - `tests/unit/storage/sqlite-refactor-harness.test.ts > linkEventToTask associates events with tasks` 失败为 pre-existing,与 M7 无关,未触碰。
@@ -0,0 +1,63 @@
1
+ # project-path git root 修复 Changelog
2
+
3
+ **Date**: 2026-05-18 17:15
4
+ **Spec**: docs/design/project-path-gitroot-spec-20260518-1715.md
5
+ **Status**: 完成
6
+
7
+ ## 完成清单
8
+
9
+ - [x] hook-lib.sh `resolve_project_path` — 已由前序 agent 完成
10
+ - [x] pre-tool-use.sh 改造 — 已由前序 agent 完成
11
+ - [x] post-tool-use.sh 改造 — 已由前序 agent 完成
12
+ - [x] user-prompt-submit.sh 改造 — 已由前序 agent 完成
13
+ - [x] notification.sh 改造 — 已由前序 agent 完成
14
+ - [x] stop.sh 改造 — 本次完成(source hook-lib.sh 方案 + 替换 nc 为 send_event_or_enqueue)
15
+ - [x] `src/core/utils/git.ts` — 本次新建
16
+ - [x] `src/cli/commands/executions.ts` 3 处 `process.cwd()` → `resolveGitRoot()`
17
+ - [△] `src/cli/commands/init.ts` — 未改,见下方原因
18
+ - [x] 安全网单测(7 case 全过),扩展现有 `tests/unit/hooks/resolve-project-path.test.ts`
19
+ - [△] 集成测试 — 降级方案:扩展现有单测(case 6/7),不新建独立 integration 文件
20
+
21
+ ## 关键代码定位
22
+
23
+ | 文件 | 行 | 说明 |
24
+ |------|-----|------|
25
+ | `src/hooks/hook-lib.sh:26-48` | — | `resolve_project_path` 函数体 |
26
+ | `src/hooks/stop.sh:5` | 5 | `source hook-lib.sh` 新增 |
27
+ | `src/hooks/stop.sh:13-14` | 13-14 | `RAW_CWD` + `resolve_project_path` 替换原两行 |
28
+ | `src/hooks/stop.sh:29-30` | 29-30 | `send_event_or_enqueue` 替换原 `nc` 调用 |
29
+ | `src/core/utils/git.ts:1-20` | — | `resolveGitRoot()` TS helper |
30
+ | `src/cli/commands/executions.ts:5` | 5 | import `resolveGitRoot` |
31
+ | `src/cli/commands/executions.ts:16,53,85` | 16,53,85 | 3 处 `process.cwd()` → `resolveGitRoot()` |
32
+ | `tests/unit/hooks/resolve-project-path.test.ts:99-117` | 99-117 | case 6/7 新增 |
33
+
34
+ ## 测试结果
35
+
36
+ - `tsc --noEmit`: 0 errors
37
+ - `npm test`: 558 passed / 1 failed(pre-existing: `linkEventToTask` in sqlite-refactor-harness.test.ts)
38
+ - hook 单测: 7 / 7 passed(含 2 个新增 case)
39
+ - `npm run build`: 成功
40
+
41
+ ## 实施决策
42
+
43
+ ### stop.sh: source 方案(非 inline)
44
+
45
+ `stop.sh` 原来手写 `nc` 直连 socket,未 source `hook-lib.sh`。本次采用 source 方案:
46
+ - 在文件顶部加 `source "$(dirname "$0")/hook-lib.sh"`,与其他 4 个 hook 保持一致性
47
+ - 同时将 `nc` 调用替换为 `send_event_or_enqueue`,获得"daemon 停机时自动入队"的能力
48
+ - stop.sh 在 daemon 运行时由 hook 调用,`$(dirname "$0")` 指向已安装的 hook 目录,路径可达
49
+
50
+ ### 集成测试降级方案
51
+
52
+ 选择"扩展现有单测"(case 6/7)而非新建独立 integration 文件:
53
+ - case 6 模拟 hook INPUT JSON 中 cwd 指向嵌套子目录的场景
54
+ - case 7 模拟 hook INPUT cwd 为空时 fallback 到 shell $PWD 的场景
55
+ - 覆盖核心路径,避免端到端测试引入 daemon 启动复杂度
56
+
57
+ ### init.ts: 未改
58
+
59
+ `init.ts` 不操作项目级路径(`.claude-forge/` 子目录)。它只操作全局 `FORGE_HOME`(`~/.claude-forge/`)和 Claude Code 的全局 settings.json。`initProject` / `template-manager` 调用不在 init.ts 中出现。无需修改。
60
+
61
+ ## 已知问题
62
+
63
+ - `linkEventToTask` 测试失败为 pre-existing,与本次改动无关
@@ -0,0 +1,35 @@
1
+ # task active GC 修复 Changelog
2
+
3
+ **Date**: 2026-05-18 17:45
4
+ **Spec**: docs/design/task-active-gc-spec-20260518-1745.md
5
+ **Status**: 完成
6
+
7
+ ## 完成清单
8
+ - [x] storage completeStaleActiveTasks
9
+ - [x] sqlite facade 转发
10
+ - [x] task-segmenter completeCurrentTask recover fallback
11
+ - [x] daemon staleTaskGcInterval (5 分钟一次,idle 10 分钟阈值)
12
+ - [x] shutdown clearInterval
13
+ - [x] 单测 4 case + recover fallback 3 case
14
+
15
+ ## 关键代码定位
16
+
17
+ - `src/core/storage/tasks.ts:144-155` — `completeStaleActiveTasks` 方法
18
+ - `src/core/storage/sqlite.ts:183-185` — facade 代理方法
19
+ - `src/daemon/services/task-segmenter.ts:78` — `completeCurrentTask` recover fallback
20
+ - `src/daemon/index.ts:118-131` — `staleTaskGcInterval` 注册(STALE_TASK_GC_INTERVAL=5min,STALE_TASK_IDLE_MINUTES=10)
21
+ - `src/daemon/index.ts:251` — `clearInterval(staleTaskGcInterval)` 在 shutdown 中
22
+
23
+ ## 测试结果
24
+ - tsc: 0 errors
25
+ - npm test: 565 passed / 1 failed (pre-existing `linkEventToTask` in sqlite-refactor-harness.test.ts)
26
+ - 新增测试: 7 cases all passed (4 storage GC + 3 segmenter recover)
27
+
28
+ ## 验证 bug 修复
29
+
30
+ - 场景 A(ESC 中断,Stop hook 不触发):5 分钟 GC 轮询后自动将 idle > 10 分钟的 active task 转 completed
31
+ - 场景 B(daemon 重启后 Stop hook 触发但 Map 空):completeCurrentTask recover fallback 从 DB 恢复并关闭任务
32
+ - 场景 C(用户永不再 prompt 旧 session):GC 兜底,idle 10 分钟后自动关闭
33
+
34
+ ## 已知问题
35
+ - `linkEventToTask` pre-existing failure in `sqlite-refactor-harness.test.ts` — 与本次改动无关
@@ -0,0 +1,39 @@
1
+ # Changelog: task-title-summary — 2026-05-18 11:30
2
+
3
+ ## Summary
4
+
5
+ Frontend-only fix: Tasks list and TaskDetail header now display the human-readable
6
+ `<summary>` extracted from `<task-notification>` XML titles instead of the raw blob.
7
+
8
+ ## Files Changed
9
+
10
+ ### New
11
+
12
+ - `web/src/utils/task-title.ts` — pure helper `normalizeTaskTitle(title)`
13
+ - `tests/unit/web/task-title-contract.test.ts` — 19 contract tests (TDD, written before implementation)
14
+ - `docs/reviews/task-title-summary.md` — fix report
15
+
16
+ ### Modified
17
+
18
+ - `web/src/pages/Tasks.tsx`
19
+ - Import `normalizeTaskTitle` from `../utils/task-title`
20
+ - Line 340: `{task.title}` → `{normalizeTaskTitle(task.title)}`
21
+
22
+ - `web/src/pages/TaskDetail.tsx`
23
+ - Import `normalizeTaskTitle` from `../utils/task-title`
24
+ - Line 205: `{data.title}` → `{normalizeTaskTitle(data.title)}`
25
+
26
+ ## Constraints Respected
27
+
28
+ - Backend untouched (task-segmenter, routes/tasks.ts, storage)
29
+ - No new npm dependencies
30
+ - TypeScript strict — zero errors
31
+ - Pure function, no React dependency
32
+ - Pre-existing test failure (sqlite-refactor-harness / uuid) is unrelated and pre-dates this change
33
+
34
+ ## Workflow
35
+
36
+ harness-hotfix: safety-net → fix → verify
37
+ - safety-net: wrote 19 failing contract tests
38
+ - fix: implemented normalizeTaskTitle, wired into Tasks.tsx + TaskDetail.tsx, all 19 tests green
39
+ - verify: web build ✓, tsc --noEmit ✓ (both root + web), 378/379 tests pass
@@ -0,0 +1,22 @@
1
+ # Changelog: tasks-detail-back-loses-filters
2
+
3
+ **Date**: 2026-05-18 11:00
4
+ **Harness workflow**: safety-net → fix → verify
5
+ **skill_invoke**: MCP not invoked (direct execution per harness instructions)
6
+
7
+ ---
8
+
9
+ ## 2026-05-18
10
+
11
+ - **10:06** Safety-net established — created `tests/unit/web/navigation-back-contract.test.ts` with 21 characterization tests locking the `resolveBackTo` fallback and happy-path behaviours before any code was changed.
12
+
13
+ - **10:08** Fix applied:
14
+ - Created `web/src/utils/navigation.ts` — exports `resolveBackTo(state: unknown, fallback='/tasks'): string` with strict type narrowing (no `as any`).
15
+ - `web/src/pages/Tasks.tsx` — added `useLocation` import; passed `{ state: { from: location.pathname + location.search } }` on task navigation.
16
+ - `web/src/pages/TaskDetail.tsx` — added `useLocation` + `resolveBackTo` imports; computed `backTo = resolveBackTo(location.state)`; replaced both absolute `<Link to="/tasks">` (error branch + success branch) with `<Link to={backTo}>`.
17
+
18
+ - **10:09** Verification:
19
+ - `web npm run build`: clean (tsc + vite, 0 errors).
20
+ - `npx tsc --noEmit` (root + web): 0 errors.
21
+ - Full test suite: 359/360 pass; 1 pre-existing failure in `sqlite-refactor-harness.test.ts` (confirmed present before our changes via `git stash` baseline run).
22
+ - Fix report written to `docs/reviews/tasks-detail-back-loses-filters.md`.
@@ -0,0 +1,56 @@
1
+ # Changelog: Tasks Page White Screen Hotfix
2
+
3
+ **Date**: 2026-05-18 10:15
4
+ **Bug slug**: tasks-page-white-screen
5
+ **Workflow**: harness-hotfix (safety-net → fix → verify)
6
+
7
+ ## Phase 1: safety-net
8
+
9
+ Created `tests/unit/web/tasks-component-contract.test.ts` with 15 characterization tests:
10
+ - Documents the broken consumer behaviour (TypeError on `.filter` of `TaskPage` object)
11
+ - Locks the correct `{ items, total, has_more }` API contract
12
+ - Tests pagination parameter logic
13
+
14
+ All 15 tests pass immediately (they lock current correct contract + document the defect pattern).
15
+
16
+ ## Phase 2: fix
17
+
18
+ ### Files Modified
19
+
20
+ **`web/src/pages/Tasks.tsx`** — full rewrite
21
+ - Fixed `fetchTasks` return type from `Task[]` to `TaskPage`
22
+ - Destructure `data.items`, `data.total`, `data.has_more` correctly
23
+ - Added URL sync via `useSearchParams` (page, size, project, preset, from, to, search)
24
+ - Added project multi-select dropdown (fetches `/api/tasks/projects`)
25
+ - Added time presets (1h / 24h / 7d)
26
+ - Added pagination controls (prev/next + page size selector 20/50/100)
27
+ - Added "共 N 条" total count display
28
+ - Added `shortPath()` helper to truncate project paths to last 2 segments
29
+ - Tailwind utility classes only, no inline styles
30
+
31
+ **`CLAUDE.md`** (line 184)
32
+ - Changed: `原生 HTML + Vanilla JS(单文件 SPA),无打包`
33
+ - To: `React 18 + Vite + Tailwind + react-router-dom + react-query + Recharts(\`web/\` 目录,build 后被 cp 到 dist/web/static/)`
34
+
35
+ **`src/claudemd/claudemd-generator.ts`** (line 737)
36
+ - Same correction in embedded template, backtick escaped to `\`` to avoid breaking the template literal
37
+
38
+ ### No Backend Changes
39
+
40
+ `App.tsx` already had correct `<Route path="tasks">` config.
41
+ `Layout.tsx` already had Tasks nav item with `ListTodo` icon.
42
+ All backend routes (`/api/tasks`, `/api/tasks/projects`) were correct and untouched.
43
+
44
+ ## Phase 3: verify
45
+
46
+ - `web/` TypeScript: clean
47
+ - Root TypeScript: clean (after escaping backtick in template literal)
48
+ - `web/npm run build`: success, `Tasks-D98y3WV2.js` chunk produced
49
+ - Root `npm run build`: success, chunk copied to `dist/web/static/assets/`
50
+ - Test results: 338/339 pass (1 pre-existing failure in sqlite-refactor-harness, unrelated)
51
+ - `curl http://127.0.0.1:3721/` → 200 OK, HTML contains 8 vite/module references
52
+ - API response confirmed: `{"items":[...],"total":393,"has_more":true}`
53
+
54
+ ## Action Required
55
+
56
+ Run `./scripts/dev-daemon.sh restart` to serve the updated bundle.
@@ -0,0 +1,92 @@
1
+ # Fix Report: task-title-summary
2
+
3
+ **Bug slug**: `task-title-summary`
4
+ **Date**: 2026-05-18
5
+ **Workflow**: harness-hotfix (safety-net → fix → verify)
6
+
7
+ ---
8
+
9
+ ## Root Cause
10
+
11
+ Sub-agent completion callbacks are pushed to the session as a `UserPromptSubmit`
12
+ event whose content is the raw `<task-notification>` XML blob. The
13
+ `task-segmenter` uses the first user-prompt content as the task `title`, so
14
+ tasks spawned from these callbacks end up with a title like:
15
+
16
+ ```
17
+ <task-notification>
18
+ <task-id>aefe161c…</task-id>
19
+
20
+ <summary>Agent "Hotfix Tasks 返回丢筛选" completed</summary>
21
+
22
+ </task-notification>
23
+ ```
24
+
25
+ The frontend rendered the literal XML string in both the Tasks list (`h3`) and
26
+ the TaskDetail header (`h1`), making the task unrecognisable.
27
+
28
+ **Backend was intentionally left unchanged** — the raw XML is the ground truth.
29
+ The fix lives entirely in the frontend presentation layer.
30
+
31
+ ---
32
+
33
+ ## Fix Points
34
+
35
+ | File | Change |
36
+ |------|--------|
37
+ | `web/src/utils/task-title.ts` | **New file** — pure helper `normalizeTaskTitle()` |
38
+ | `web/src/pages/Tasks.tsx` | Import + wrap `{task.title}` → `{normalizeTaskTitle(task.title)}` in list `<h3>` |
39
+ | `web/src/pages/TaskDetail.tsx` | Import + wrap `{data.title}` → `{normalizeTaskTitle(data.title)}` in detail `<h1>` |
40
+
41
+ ### `normalizeTaskTitle` logic (MAX_LEN = 80)
42
+
43
+ 1. `null / undefined / whitespace` → `"(无标题)"`
44
+ 2. Title starts with `<task-notification>`:
45
+ - Has `<summary>` with non-blank content → extract, trim, truncate + `…` if > 80 chars
46
+ - No `<summary>` or blank → `"(子任务回调)"`
47
+ 3. Regular title → trim, truncate + `…` if > 80 chars
48
+
49
+ ---
50
+
51
+ ## New Tests
52
+
53
+ File: `tests/unit/web/task-title-contract.test.ts`
54
+
55
+ **19 test cases** covering:
56
+
57
+ | Group | Cases |
58
+ |-------|-------|
59
+ | Passthrough | normal title, English title, whitespace trim |
60
+ | Fallback | empty string, whitespace-only, null, undefined |
61
+ | XML extraction | full blob, missing summary, bare tag, emoji+Chinese, quotes, first-match-wins |
62
+ | Truncation | summary > 80, summary = 80, plain title > 80, plain title = 80 |
63
+ | Summary whitespace | padded summary, whitespace-only summary |
64
+
65
+ ---
66
+
67
+ ## Regression / Build Verification
68
+
69
+ ```
70
+ Tests: 19 passed (task-title-contract.test.ts — all green)
71
+ Suite: 378 passed, 1 pre-existing failure (sqlite-refactor-harness / uuid validation,
72
+ unrelated to this change, present before this fix)
73
+
74
+ web build: ✓ built in 2.50s (no errors)
75
+ new chunk: dist/assets/task-title-Bc8tnLXB.js (0.36 kB gzip: 0.30 kB)
76
+ Tasks chunk hash: Tasks-C8JHCGTC.js
77
+ TaskDetail chunk hash: TaskDetail-DYEk8pNw.js
78
+
79
+ TypeScript: npx tsc --noEmit → 0 errors (root)
80
+ npx tsc --noEmit → 0 errors (web/)
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Notes
86
+
87
+ - `normalizeTaskTitle` is a pure function with zero React dependency — safe to
88
+ test in Node environment without jsdom.
89
+ - The helper intentionally **does not** modify stored data. Backend titles remain
90
+ unchanged; this is purely a display-layer normalisation.
91
+ - The first `<summary>` match is used defensively in case of malformed XML with
92
+ duplicate tags.
@@ -0,0 +1,58 @@
1
+ # Fix Report: tasks-detail-back-loses-filters
2
+
3
+ **Date**: 2026-05-18
4
+ **Workflow**: harness-hotfix (safety-net → fix → verify)
5
+
6
+ ---
7
+
8
+ ## Root Cause
9
+
10
+ `TaskDetail.tsx` used `<Link to="/tasks">` — a hard absolute path — for both the
11
+ error-state and success-state "back" buttons (lines 161 and 195 of the original
12
+ file). React Router resolves this to `/tasks` with no query string, discarding
13
+ whatever filter state the user had on the Tasks page.
14
+
15
+ Concretely: a user at `/tasks?project=foo&page=2` clicks a task, lands on
16
+ `/tasks/<id>`, then clicks "返回" → router navigates to `/tasks` (no `?project=foo&page=2`).
17
+ `useSearchParams()` returns empty values, so the Tasks page re-renders showing
18
+ all tasks from page 1.
19
+
20
+ ---
21
+
22
+ ## Fix Points
23
+
24
+ | File | Change |
25
+ |------|--------|
26
+ | `web/src/utils/navigation.ts` (new) | Pure function `resolveBackTo(state, fallback='/tasks'): string` with full type narrowing — no `as any`. |
27
+ | `web/src/pages/Tasks.tsx` | Added `useLocation` import. Added `const location = useLocation()` in component body. Changed `navigate(\`/tasks/${task.id}\`)` to `navigate(\`/tasks/${task.id}\`, { state: { from: location.pathname + location.search } })`. |
28
+ | `web/src/pages/TaskDetail.tsx` | Added `useLocation` + `resolveBackTo` imports. Added `const location = useLocation()` and `const backTo = resolveBackTo(location.state)` in component body. Replaced both `<Link to="/tasks">` with `<Link to={backTo}>` (error branch line ~161, success branch line ~195). |
29
+
30
+ ---
31
+
32
+ ## New Tests
33
+
34
+ **File**: `tests/unit/web/navigation-back-contract.test.ts`
35
+
36
+ 21 contract tests across 4 suites:
37
+
38
+ 1. **fallback path** (7 tests) — `state` is `null`, `undefined`, `{}`, `{ from: undefined }`, `{ from: '' }`, custom fallback with `null`, custom fallback with `{}`.
39
+ 2. **happy path** (5 tests) — `state.from` carries full query string with project filter, page number, preset, search; plain `/tasks` (no query string).
40
+ 3. **type safety** (6 tests) — `state.from` is number, array, object, `null`; `state` itself is a number or string — all fall back gracefully.
41
+ 4. **navigation state construction** (3 tests) — verifies the `location.pathname + location.search` encoding round-trips without mutation; handles empty search string.
42
+
43
+ Tests import from the real `web/src/utils/navigation.ts` module.
44
+
45
+ ---
46
+
47
+ ## Regression Test Results
48
+
49
+ | Run | Files | Tests | Failures |
50
+ |-----|-------|-------|---------|
51
+ | Pre-change baseline | 1 failed / 29 passed | 1 failed / 359 passed | `sqlite-refactor-harness.test.ts` (pre-existing, unrelated) |
52
+ | Post-fix | 1 failed / 29 passed | 1 failed / 359 passed | same pre-existing failure only |
53
+
54
+ - `npm run build` (web/): clean — 0 TypeScript errors, 0 Vite errors
55
+ - `npx tsc --noEmit` (project root): 0 errors
56
+ - `npx tsc --noEmit` (web/): 0 errors (run as part of `npm run build`)
57
+ - New safety-net: 21/21 pass
58
+ - Existing tasks contract: 15/15 pass (no regression)
@@ -0,0 +1,126 @@
1
+ # Hotfix Report: Tasks Page White Screen
2
+
3
+ **Bug slug**: tasks-page-white-screen
4
+ **Date**: 2026-05-18
5
+ **Severity**: P0 (production white screen on Tasks nav item click)
6
+
7
+ ---
8
+
9
+ ## Root Cause
10
+
11
+ ### Error Chain
12
+
13
+ 1. `web/src/App.tsx:12` lazy-imports `./pages/Tasks`
14
+ 2. `Tasks.tsx` (pre-fix) called `GET /api/tasks` and did `return res.json()` — treating the response body as `Task[]`
15
+ 3. `GET /api/tasks` actually returns `{ items: Task[], total: number, has_more: boolean }` — a paginated wrapper (`TaskPage`), not a bare array
16
+ 4. The component called `tasks.filter(...)` on the `TaskPage` object, which has no `.filter` method → `TypeError: tasks.filter is not a function`
17
+ 5. React's error boundary was not configured → unhandled `TypeError` inside `<Suspense>` → white screen
18
+
19
+ ### Why This Shape Mismatch Existed
20
+
21
+ The backend `/api/tasks` route was upgraded (v8.50.6 implementation task `tasks-filter-pagination`) to add filter + pagination support, returning `{ items, total, has_more }`. The `Tasks.tsx` component was a pre-existing stub that was never updated to consume the new paginated shape.
22
+
23
+ ### Why v8.50.6 Spec Walked Off Target
24
+
25
+ The planner agent generating the v8.50.6 spec analyzed `src/web/routes/tasks.ts` (backend) but did not inspect `web/src/pages/Tasks.tsx` (frontend). It documented the backend correctly but left the frontend in an inconsistent state. Additionally, `CLAUDE.md` was incorrectly updated to say the frontend uses "原生 HTML + Vanilla JS" (Vanilla JS) rather than React + Vite — this misdirected future agents away from looking in `web/`.
26
+
27
+ **How to avoid next time**:
28
+ - When modifying a backend API shape, always grep for frontend consumers in `web/src/` before closing the task
29
+ - `CLAUDE.md` tech stack description is a critical navigation signal for agents — any incorrect entry should be treated as a P1 issue
30
+ - Add a CI check or spec gate: "if API response shape changes, verify frontend page component is updated"
31
+
32
+ ---
33
+
34
+ ## Fix Points
35
+
36
+ ### Modified Files
37
+
38
+ | File | Lines | Change |
39
+ |------|-------|--------|
40
+ | `web/src/pages/Tasks.tsx` | full rewrite (~290 lines) | Fix `fetchTasks` to return `TaskPage`, destructure `items`/`total`/`has_more`, add pagination + project filter + time presets + URL sync via `useSearchParams` |
41
+ | `CLAUDE.md` | line 184 | Restore frontend description to React 18 + Vite + Tailwind + react-router-dom + react-query + Recharts |
42
+ | `src/claudemd/claudemd-generator.ts` | line 737 | Same correction in the embedded template (escaped backtick) |
43
+
44
+ ### New Files
45
+
46
+ | File | Purpose |
47
+ |------|---------|
48
+ | `tests/unit/web/tasks-component-contract.test.ts` | Safety-net: 15 tests locking the `{ items, total, has_more }` API contract and documenting the broken consumer behaviour |
49
+
50
+ ### Key Code Fix
51
+
52
+ Before (broken):
53
+ ```typescript
54
+ async function fetchTasks(): Promise<Task[]> {
55
+ const res = await fetch('/api/tasks')
56
+ if (!res.ok) throw new Error('Failed to fetch tasks')
57
+ return res.json() // returns TaskPage object, not Task[]
58
+ }
59
+ // ...
60
+ const { data: tasks } = useQuery({ queryFn: fetchTasks })
61
+ tasks.filter(...) // TypeError: tasks.filter is not a function
62
+ ```
63
+
64
+ After (fixed):
65
+ ```typescript
66
+ async function fetchTasks(params: URLSearchParams): Promise<TaskPage> {
67
+ const res = await fetch(`/api/tasks?${params.toString()}`)
68
+ if (!res.ok) throw new Error('Failed to fetch tasks')
69
+ return res.json() as Promise<TaskPage>
70
+ }
71
+ // ...
72
+ const { data } = useQuery({ queryFn: () => fetchTasks(queryParams) })
73
+ const tasks = data?.items ?? [] // correct: array from paginated wrapper
74
+ const total = data?.total ?? 0
75
+ ```
76
+
77
+ ---
78
+
79
+ ## New Tests (Safety-Net)
80
+
81
+ File: `tests/unit/web/tasks-component-contract.test.ts`
82
+
83
+ 15 tests in 4 groups:
84
+ 1. **API response shape** (5 tests) — asserts `{ items, total, has_more }` structure
85
+ 2. **[CHAR] broken consumer** (2 tests) — documents the pre-fix white screen defect: `consumeTasksResponseBROKEN` returns object not array, `.filter()` throws `TypeError`
86
+ 3. **Correct consumer** (5 tests) — contract the fix satisfies: array extraction, field access, empty page
87
+ 4. **Pagination parameters** (3 tests) — default limit 50, valid page sizes [20, 50, 100], offset formula
88
+
89
+ ---
90
+
91
+ ## Regression Test Results
92
+
93
+ ```
94
+ Test Files 1 failed | 28 passed (29)
95
+ Tests 1 failed | 338 passed (339)
96
+ ```
97
+
98
+ The 1 pre-existing failure (`sqlite-refactor-harness.test.ts:166 linkEventToTask`) was present before this hotfix and is unrelated (UUID validation in test fixture). 15 new safety-net tests all pass.
99
+
100
+ ---
101
+
102
+ ## Build Evidence
103
+
104
+ ```
105
+ web/dist/assets/Tasks-D98y3WV2.js (3.xx kB — new chunk)
106
+ dist/web/static/assets/Tasks-D98y3WV2.js (copied to production path)
107
+ ```
108
+
109
+ `curl -sI http://127.0.0.1:3721/` → `200 OK`
110
+ `curl -s http://127.0.0.1:3721/ | grep -c "vite|module"` → `8` (React app, not vanilla HTML)
111
+ `curl -s "http://127.0.0.1:3721/api/tasks?limit=1"` → `{"items":[...],"total":393,"has_more":true}`
112
+
113
+ ---
114
+
115
+ ## Verification Checklist
116
+
117
+ - [x] `web/` TypeScript: `npx tsc --noEmit` → 0 errors
118
+ - [x] Root TypeScript: `npx tsc --noEmit` → 0 errors
119
+ - [x] `web/npm run build` → succeeds, Tasks chunk present
120
+ - [x] Root `npm run build` → succeeds, chunk copied to `dist/web/static/assets/`
121
+ - [x] Safety-net tests: 15/15 pass
122
+ - [x] Full suite: 338/339 pass (pre-existing failure unrelated)
123
+ - [x] `curl http://127.0.0.1:3721/` → 200 OK, HTML contains vite module references
124
+ - [x] API returns `{ items, total, has_more }` shape confirmed
125
+ - [x] `CLAUDE.md` and `claudemd-generator.ts` frontend description corrected
126
+ - [ ] Daemon restart (requires manual: `./scripts/dev-daemon.sh restart`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@winspan/claude-forge",
3
- "version": "8.50.6",
3
+ "version": "8.51.1",
4
4
  "description": "SDLC intelligent orchestration engine for Claude Code",
5
5
  "main": "dist/cli/index.js",
6
6
  "type": "module",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "scripts": {
12
12
  "prebuild": "rm -rf dist",
13
- "build": "tsc && mkdir -p dist/hooks && cp -r src/hooks/* dist/hooks/ && chmod +x dist/hooks/*.sh && chmod +x dist/cli/index.js && cp src/core/storage/schema.sql dist/core/storage/schema.sql && mkdir -p dist/daemon/launchd && cp src/daemon/launchd/*.template dist/daemon/launchd/ && mkdir -p dist/skills/official && cp src/skills/official/*.md dist/skills/official/ && mkdir -p dist/web/static && cp -r web/dist/* dist/web/static/ 2>/dev/null || cp src/web/static/index.html dist/web/static/ && cp -r src/web/static/vendor dist/web/static/ 2>/dev/null || true",
13
+ "build": "tsc && mkdir -p dist/hooks && cp -r src/hooks/* dist/hooks/ && chmod +x dist/hooks/*.sh && chmod +x dist/cli/index.js && cp src/core/storage/schema.sql dist/core/storage/schema.sql && mkdir -p dist/daemon/launchd && cp src/daemon/launchd/*.template dist/daemon/launchd/ && mkdir -p dist/skills/official && cp src/skills/official/*.md dist/skills/official/ && mkdir -p dist/claudemd/templates && cp src/claudemd/templates/*.md dist/claudemd/templates/ && mkdir -p dist/web/static && cp -r web/dist/* dist/web/static/ 2>/dev/null || cp src/web/static/index.html dist/web/static/ && cp -r src/web/static/vendor dist/web/static/ 2>/dev/null || true && (test -f dist/core/storage/schema.sql || (echo '[build] FATAL: dist/core/storage/schema.sql missing' && exit 1)) && (test -f dist/claudemd/templates/swarm-protocol.md || (echo '[build] FATAL: dist/claudemd/templates/swarm-protocol.md missing' && exit 1))",
14
14
  "test": "vitest run",
15
15
  "test:watch": "vitest",
16
16
  "dev:daemon": "./scripts/dev-daemon.sh",