specweave 0.28.68 → 0.29.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 (318) hide show
  1. package/CLAUDE.md +3 -2
  2. package/README.md +19 -2
  3. package/dist/src/cli/commands/discrepancies.d.ts +89 -0
  4. package/dist/src/cli/commands/discrepancies.d.ts.map +1 -0
  5. package/dist/src/cli/commands/discrepancies.js +385 -0
  6. package/dist/src/cli/commands/discrepancies.js.map +1 -0
  7. package/dist/src/cli/commands/notifications.d.ts +70 -0
  8. package/dist/src/cli/commands/notifications.d.ts.map +1 -0
  9. package/dist/src/cli/commands/notifications.js +236 -0
  10. package/dist/src/cli/commands/notifications.js.map +1 -0
  11. package/dist/src/cli/commands/sync-logs.d.ts +54 -0
  12. package/dist/src/cli/commands/sync-logs.d.ts.map +1 -0
  13. package/dist/src/cli/commands/sync-logs.js +240 -0
  14. package/dist/src/cli/commands/sync-logs.js.map +1 -0
  15. package/dist/src/cli/commands/sync-monitor.d.ts +42 -0
  16. package/dist/src/cli/commands/sync-monitor.d.ts.map +1 -0
  17. package/dist/src/cli/commands/sync-monitor.js +191 -0
  18. package/dist/src/cli/commands/sync-monitor.js.map +1 -0
  19. package/dist/src/cli/helpers/init/brownfield-analysis.d.ts +45 -0
  20. package/dist/src/cli/helpers/init/brownfield-analysis.d.ts.map +1 -0
  21. package/dist/src/cli/helpers/init/brownfield-analysis.js +431 -0
  22. package/dist/src/cli/helpers/init/brownfield-analysis.js.map +1 -0
  23. package/dist/src/cli/helpers/init/index.d.ts +1 -0
  24. package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
  25. package/dist/src/cli/helpers/init/index.js +2 -0
  26. package/dist/src/cli/helpers/init/index.js.map +1 -1
  27. package/dist/src/cli/workers/brownfield-worker.d.ts +66 -0
  28. package/dist/src/cli/workers/brownfield-worker.d.ts.map +1 -0
  29. package/dist/src/cli/workers/brownfield-worker.js +417 -0
  30. package/dist/src/cli/workers/brownfield-worker.js.map +1 -0
  31. package/dist/src/core/background/brownfield-launcher.d.ts +86 -0
  32. package/dist/src/core/background/brownfield-launcher.d.ts.map +1 -0
  33. package/dist/src/core/background/brownfield-launcher.js +295 -0
  34. package/dist/src/core/background/brownfield-launcher.js.map +1 -0
  35. package/dist/src/core/background/index.d.ts +2 -0
  36. package/dist/src/core/background/index.d.ts.map +1 -1
  37. package/dist/src/core/background/index.js +2 -0
  38. package/dist/src/core/background/index.js.map +1 -1
  39. package/dist/src/core/background/types.d.ts +23 -2
  40. package/dist/src/core/background/types.d.ts.map +1 -1
  41. package/dist/src/core/config/index.d.ts +1 -0
  42. package/dist/src/core/config/index.d.ts.map +1 -1
  43. package/dist/src/core/config/index.js +1 -0
  44. package/dist/src/core/config/index.js.map +1 -1
  45. package/dist/src/core/config/types.d.ts +6 -0
  46. package/dist/src/core/config/types.d.ts.map +1 -1
  47. package/dist/src/core/config/types.js.map +1 -1
  48. package/dist/src/core/dashboard/dashboard-data.d.ts +156 -0
  49. package/dist/src/core/dashboard/dashboard-data.d.ts.map +1 -0
  50. package/dist/src/core/dashboard/dashboard-data.js +191 -0
  51. package/dist/src/core/dashboard/dashboard-data.js.map +1 -0
  52. package/dist/src/core/dashboard/index.d.ts +9 -0
  53. package/dist/src/core/dashboard/index.d.ts.map +1 -0
  54. package/dist/src/core/dashboard/index.js +9 -0
  55. package/dist/src/core/dashboard/index.js.map +1 -0
  56. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts +77 -0
  57. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts.map +1 -0
  58. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js +286 -0
  59. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js.map +1 -0
  60. package/dist/src/core/discrepancy/analyzers/index.d.ts +8 -0
  61. package/dist/src/core/discrepancy/analyzers/index.d.ts.map +1 -0
  62. package/dist/src/core/discrepancy/analyzers/index.js +8 -0
  63. package/dist/src/core/discrepancy/analyzers/index.js.map +1 -0
  64. package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts +96 -0
  65. package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts.map +1 -0
  66. package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js +247 -0
  67. package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js.map +1 -0
  68. package/dist/src/core/discrepancy/brownfield-manager.d.ts +88 -0
  69. package/dist/src/core/discrepancy/brownfield-manager.d.ts.map +1 -0
  70. package/dist/src/core/discrepancy/brownfield-manager.js +520 -0
  71. package/dist/src/core/discrepancy/brownfield-manager.js.map +1 -0
  72. package/dist/src/core/discrepancy/brownfield-types.d.ts +174 -0
  73. package/dist/src/core/discrepancy/brownfield-types.d.ts.map +1 -0
  74. package/dist/src/core/discrepancy/brownfield-types.js +11 -0
  75. package/dist/src/core/discrepancy/brownfield-types.js.map +1 -0
  76. package/dist/src/core/discrepancy/detector.d.ts +92 -0
  77. package/dist/src/core/discrepancy/detector.d.ts.map +1 -0
  78. package/dist/src/core/discrepancy/detector.js +346 -0
  79. package/dist/src/core/discrepancy/detector.js.map +1 -0
  80. package/dist/src/core/discrepancy/increment-generator.d.ts +51 -0
  81. package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -0
  82. package/dist/src/core/discrepancy/increment-generator.js +234 -0
  83. package/dist/src/core/discrepancy/increment-generator.js.map +1 -0
  84. package/dist/src/core/discrepancy/index.d.ts +18 -0
  85. package/dist/src/core/discrepancy/index.d.ts.map +1 -0
  86. package/dist/src/core/discrepancy/index.js +24 -0
  87. package/dist/src/core/discrepancy/index.js.map +1 -0
  88. package/dist/src/core/discrepancy/severity-classifier.d.ts +81 -0
  89. package/dist/src/core/discrepancy/severity-classifier.d.ts.map +1 -0
  90. package/dist/src/core/discrepancy/severity-classifier.js +289 -0
  91. package/dist/src/core/discrepancy/severity-classifier.js.map +1 -0
  92. package/dist/src/core/discrepancy/spec-parser.d.ts +74 -0
  93. package/dist/src/core/discrepancy/spec-parser.d.ts.map +1 -0
  94. package/dist/src/core/discrepancy/spec-parser.js +213 -0
  95. package/dist/src/core/discrepancy/spec-parser.js.map +1 -0
  96. package/dist/src/core/discrepancy/update-recommender.d.ts +77 -0
  97. package/dist/src/core/discrepancy/update-recommender.d.ts.map +1 -0
  98. package/dist/src/core/discrepancy/update-recommender.js +323 -0
  99. package/dist/src/core/discrepancy/update-recommender.js.map +1 -0
  100. package/dist/src/core/living-docs/living-docs-sync.d.ts +13 -16
  101. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  102. package/dist/src/core/living-docs/living-docs-sync.js +31 -112
  103. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  104. package/dist/src/core/logs/index.d.ts +10 -0
  105. package/dist/src/core/logs/index.d.ts.map +1 -0
  106. package/dist/src/core/logs/index.js +10 -0
  107. package/dist/src/core/logs/index.js.map +1 -0
  108. package/dist/src/core/logs/log-aggregator.d.ts +130 -0
  109. package/dist/src/core/logs/log-aggregator.d.ts.map +1 -0
  110. package/dist/src/core/logs/log-aggregator.js +206 -0
  111. package/dist/src/core/logs/log-aggregator.js.map +1 -0
  112. package/dist/src/core/logs/log-exporter.d.ts +81 -0
  113. package/dist/src/core/logs/log-exporter.d.ts.map +1 -0
  114. package/dist/src/core/logs/log-exporter.js +141 -0
  115. package/dist/src/core/logs/log-exporter.js.map +1 -0
  116. package/dist/src/core/notifications/command-integration.d.ts +82 -0
  117. package/dist/src/core/notifications/command-integration.d.ts.map +1 -0
  118. package/dist/src/core/notifications/command-integration.js +80 -0
  119. package/dist/src/core/notifications/command-integration.js.map +1 -0
  120. package/dist/src/core/notifications/index.d.ts +12 -0
  121. package/dist/src/core/notifications/index.d.ts.map +1 -0
  122. package/dist/src/core/notifications/index.js +12 -0
  123. package/dist/src/core/notifications/index.js.map +1 -0
  124. package/dist/src/core/notifications/notification-display.d.ts +70 -0
  125. package/dist/src/core/notifications/notification-display.d.ts.map +1 -0
  126. package/dist/src/core/notifications/notification-display.js +177 -0
  127. package/dist/src/core/notifications/notification-display.js.map +1 -0
  128. package/dist/src/core/notifications/notification-manager.d.ts +126 -0
  129. package/dist/src/core/notifications/notification-manager.d.ts.map +1 -0
  130. package/dist/src/core/notifications/notification-manager.js +287 -0
  131. package/dist/src/core/notifications/notification-manager.js.map +1 -0
  132. package/dist/src/core/notifications/notification-types.d.ts +159 -0
  133. package/dist/src/core/notifications/notification-types.d.ts.map +1 -0
  134. package/dist/src/core/notifications/notification-types.js +93 -0
  135. package/dist/src/core/notifications/notification-types.js.map +1 -0
  136. package/dist/src/core/scheduler/index.d.ts +11 -0
  137. package/dist/src/core/scheduler/index.d.ts.map +1 -0
  138. package/dist/src/core/scheduler/index.js +11 -0
  139. package/dist/src/core/scheduler/index.js.map +1 -0
  140. package/dist/src/core/scheduler/job-scheduler.d.ts +179 -0
  141. package/dist/src/core/scheduler/job-scheduler.d.ts.map +1 -0
  142. package/dist/src/core/scheduler/job-scheduler.js +282 -0
  143. package/dist/src/core/scheduler/job-scheduler.js.map +1 -0
  144. package/dist/src/core/scheduler/schedule-persistence.d.ts +83 -0
  145. package/dist/src/core/scheduler/schedule-persistence.d.ts.map +1 -0
  146. package/dist/src/core/scheduler/schedule-persistence.js +180 -0
  147. package/dist/src/core/scheduler/schedule-persistence.js.map +1 -0
  148. package/dist/src/core/scheduler/scheduled-job.d.ts +188 -0
  149. package/dist/src/core/scheduler/scheduled-job.d.ts.map +1 -0
  150. package/dist/src/core/scheduler/scheduled-job.js +182 -0
  151. package/dist/src/core/scheduler/scheduled-job.js.map +1 -0
  152. package/dist/src/core/sync/permission-enforcer.d.ts +206 -0
  153. package/dist/src/core/sync/permission-enforcer.d.ts.map +1 -0
  154. package/dist/src/core/sync/permission-enforcer.js +268 -0
  155. package/dist/src/core/sync/permission-enforcer.js.map +1 -0
  156. package/dist/src/core/sync/sync-audit-logger.d.ts +217 -0
  157. package/dist/src/core/sync/sync-audit-logger.d.ts.map +1 -0
  158. package/dist/src/core/sync/sync-audit-logger.js +327 -0
  159. package/dist/src/core/sync/sync-audit-logger.js.map +1 -0
  160. package/dist/src/core/sync/sync-interceptor.d.ts +190 -0
  161. package/dist/src/core/sync/sync-interceptor.d.ts.map +1 -0
  162. package/dist/src/core/sync/sync-interceptor.js +224 -0
  163. package/dist/src/core/sync/sync-interceptor.js.map +1 -0
  164. package/dist/src/core/types/increment-metadata.d.ts +5 -2
  165. package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
  166. package/dist/src/core/types/sync-config.d.ts +267 -0
  167. package/dist/src/core/types/sync-config.d.ts.map +1 -0
  168. package/dist/src/core/types/sync-config.js +304 -0
  169. package/dist/src/core/types/sync-config.js.map +1 -0
  170. package/dist/src/hooks/index.d.ts +11 -0
  171. package/dist/src/hooks/index.d.ts.map +1 -0
  172. package/dist/src/hooks/index.js +11 -0
  173. package/dist/src/hooks/index.js.map +1 -0
  174. package/dist/src/hooks/platform.d.ts +125 -0
  175. package/dist/src/hooks/platform.d.ts.map +1 -0
  176. package/dist/src/hooks/platform.js +325 -0
  177. package/dist/src/hooks/platform.js.map +1 -0
  178. package/dist/src/hooks/processor.d.ts +20 -0
  179. package/dist/src/hooks/processor.d.ts.map +1 -0
  180. package/dist/src/hooks/processor.js +317 -0
  181. package/dist/src/hooks/processor.js.map +1 -0
  182. package/dist/src/hooks/scheduler-startup.d.ts +19 -0
  183. package/dist/src/hooks/scheduler-startup.d.ts.map +1 -0
  184. package/dist/src/hooks/scheduler-startup.js +92 -0
  185. package/dist/src/hooks/scheduler-startup.js.map +1 -0
  186. package/dist/src/hooks/session-start.d.ts +16 -0
  187. package/dist/src/hooks/session-start.d.ts.map +1 -0
  188. package/dist/src/hooks/session-start.js +92 -0
  189. package/dist/src/hooks/session-start.js.map +1 -0
  190. package/dist/src/importers/duplicate-detector.d.ts +13 -2
  191. package/dist/src/importers/duplicate-detector.d.ts.map +1 -1
  192. package/dist/src/importers/duplicate-detector.js +21 -2
  193. package/dist/src/importers/duplicate-detector.js.map +1 -1
  194. package/dist/src/importers/item-converter.d.ts +41 -2
  195. package/dist/src/importers/item-converter.d.ts.map +1 -1
  196. package/dist/src/importers/item-converter.js +225 -38
  197. package/dist/src/importers/item-converter.js.map +1 -1
  198. package/dist/src/living-docs/fs-id-allocator.d.ts +7 -0
  199. package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
  200. package/dist/src/living-docs/fs-id-allocator.js +30 -4
  201. package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
  202. package/dist/src/sync/ado-sync-wrapper.d.ts +137 -0
  203. package/dist/src/sync/ado-sync-wrapper.d.ts.map +1 -0
  204. package/dist/src/sync/ado-sync-wrapper.js +148 -0
  205. package/dist/src/sync/ado-sync-wrapper.js.map +1 -0
  206. package/dist/src/sync/github-sync-wrapper.d.ts +195 -0
  207. package/dist/src/sync/github-sync-wrapper.d.ts.map +1 -0
  208. package/dist/src/sync/github-sync-wrapper.js +220 -0
  209. package/dist/src/sync/github-sync-wrapper.js.map +1 -0
  210. package/dist/src/sync/jira-sync-wrapper.d.ts +155 -0
  211. package/dist/src/sync/jira-sync-wrapper.d.ts.map +1 -0
  212. package/dist/src/sync/jira-sync-wrapper.js +175 -0
  213. package/dist/src/sync/jira-sync-wrapper.js.map +1 -0
  214. package/dist/src/utils/feature-id-derivation.d.ts +58 -0
  215. package/dist/src/utils/feature-id-derivation.d.ts.map +1 -0
  216. package/dist/src/utils/feature-id-derivation.js +77 -0
  217. package/dist/src/utils/feature-id-derivation.js.map +1 -0
  218. package/package.json +3 -1
  219. package/plugins/specweave/commands/specweave-discrepancies.md +141 -0
  220. package/plugins/specweave/commands/specweave-discrepancy-to-increment.md +160 -0
  221. package/plugins/specweave/commands/specweave-jobs.md +45 -2
  222. package/plugins/specweave/commands/specweave-notifications.md +92 -0
  223. package/plugins/specweave/commands/specweave-sync-logs.md +131 -0
  224. package/plugins/specweave/commands/specweave-sync-monitor.md +57 -0
  225. package/plugins/specweave/hooks/hooks.json +3 -3
  226. package/plugins/specweave/hooks/lib/scheduler-startup.sh +72 -0
  227. package/plugins/specweave/hooks/universal/dispatcher.mjs +246 -0
  228. package/plugins/specweave/hooks/universal/session-start.cmd +16 -0
  229. package/plugins/specweave/hooks/universal/session-start.ps1 +16 -0
  230. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +14 -5
  231. package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +5 -2
  232. package/plugins/specweave/skills/discrepancy-viewer.md +154 -0
  233. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +46 -0
  234. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +69 -0
  235. package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts +0 -26
  236. package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts.map +0 -1
  237. package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +0 -249
  238. package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +0 -1
  239. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +0 -28
  240. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +0 -1
  241. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +0 -156
  242. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +0 -1
  243. package/dist/src/core/sync/bidirectional-engine.d.ts +0 -119
  244. package/dist/src/core/sync/bidirectional-engine.d.ts.map +0 -1
  245. package/dist/src/core/sync/bidirectional-engine.js +0 -359
  246. package/dist/src/core/sync/bidirectional-engine.js.map +0 -1
  247. package/dist/src/core/sync/conflict-resolver.d.ts +0 -66
  248. package/dist/src/core/sync/conflict-resolver.d.ts.map +0 -1
  249. package/dist/src/core/sync/conflict-resolver.js +0 -108
  250. package/dist/src/core/sync/conflict-resolver.js.map +0 -1
  251. package/dist/src/core/sync/enhanced-content-builder.d.ts +0 -55
  252. package/dist/src/core/sync/enhanced-content-builder.d.ts.map +0 -1
  253. package/dist/src/core/sync/enhanced-content-builder.js +0 -203
  254. package/dist/src/core/sync/enhanced-content-builder.js.map +0 -1
  255. package/dist/src/core/sync/folder-mapper.d.ts +0 -71
  256. package/dist/src/core/sync/folder-mapper.d.ts.map +0 -1
  257. package/dist/src/core/sync/folder-mapper.js +0 -203
  258. package/dist/src/core/sync/folder-mapper.js.map +0 -1
  259. package/dist/src/core/sync/label-detector.d.ts +0 -66
  260. package/dist/src/core/sync/label-detector.d.ts.map +0 -1
  261. package/dist/src/core/sync/label-detector.js +0 -224
  262. package/dist/src/core/sync/label-detector.js.map +0 -1
  263. package/dist/src/core/sync/performance-optimizer.d.ts +0 -153
  264. package/dist/src/core/sync/performance-optimizer.d.ts.map +0 -1
  265. package/dist/src/core/sync/performance-optimizer.js +0 -220
  266. package/dist/src/core/sync/performance-optimizer.js.map +0 -1
  267. package/dist/src/core/sync/profile-selector.d.ts +0 -52
  268. package/dist/src/core/sync/profile-selector.d.ts.map +0 -1
  269. package/dist/src/core/sync/profile-selector.js +0 -179
  270. package/dist/src/core/sync/profile-selector.js.map +0 -1
  271. package/dist/src/core/sync/profile-validator.d.ts +0 -52
  272. package/dist/src/core/sync/profile-validator.d.ts.map +0 -1
  273. package/dist/src/core/sync/profile-validator.js +0 -170
  274. package/dist/src/core/sync/profile-validator.js.map +0 -1
  275. package/dist/src/core/sync/rate-limiter.d.ts +0 -116
  276. package/dist/src/core/sync/rate-limiter.d.ts.map +0 -1
  277. package/dist/src/core/sync/rate-limiter.js +0 -308
  278. package/dist/src/core/sync/rate-limiter.js.map +0 -1
  279. package/dist/src/core/sync/retry-handler.d.ts +0 -98
  280. package/dist/src/core/sync/retry-handler.d.ts.map +0 -1
  281. package/dist/src/core/sync/retry-handler.js +0 -196
  282. package/dist/src/core/sync/retry-handler.js.map +0 -1
  283. package/dist/src/core/sync/retry-logic.d.ts +0 -64
  284. package/dist/src/core/sync/retry-logic.d.ts.map +0 -1
  285. package/dist/src/core/sync/retry-logic.js +0 -165
  286. package/dist/src/core/sync/retry-logic.js.map +0 -1
  287. package/dist/src/core/sync/status-cache.d.ts +0 -91
  288. package/dist/src/core/sync/status-cache.d.ts.map +0 -1
  289. package/dist/src/core/sync/status-cache.js +0 -140
  290. package/dist/src/core/sync/status-cache.js.map +0 -1
  291. package/dist/src/core/sync/status-mapper.d.ts +0 -69
  292. package/dist/src/core/sync/status-mapper.d.ts.map +0 -1
  293. package/dist/src/core/sync/status-mapper.js +0 -90
  294. package/dist/src/core/sync/status-mapper.js.map +0 -1
  295. package/dist/src/core/sync/status-sync-engine.d.ts +0 -162
  296. package/dist/src/core/sync/status-sync-engine.d.ts.map +0 -1
  297. package/dist/src/core/sync/status-sync-engine.js +0 -347
  298. package/dist/src/core/sync/status-sync-engine.js.map +0 -1
  299. package/dist/src/core/sync/sync-event-logger.d.ts +0 -113
  300. package/dist/src/core/sync/sync-event-logger.d.ts.map +0 -1
  301. package/dist/src/core/sync/sync-event-logger.js +0 -141
  302. package/dist/src/core/sync/sync-event-logger.js.map +0 -1
  303. package/dist/src/core/sync/time-range-selector.d.ts +0 -48
  304. package/dist/src/core/sync/time-range-selector.d.ts.map +0 -1
  305. package/dist/src/core/sync/time-range-selector.js +0 -224
  306. package/dist/src/core/sync/time-range-selector.js.map +0 -1
  307. package/dist/src/core/sync/types.d.ts +0 -52
  308. package/dist/src/core/sync/types.d.ts.map +0 -1
  309. package/dist/src/core/sync/types.js +0 -5
  310. package/dist/src/core/sync/types.js.map +0 -1
  311. package/dist/src/core/sync/workflow-detector.d.ts +0 -95
  312. package/dist/src/core/sync/workflow-detector.d.ts.map +0 -1
  313. package/dist/src/core/sync/workflow-detector.js +0 -175
  314. package/dist/src/core/sync/workflow-detector.js.map +0 -1
  315. package/plugins/specweave-github/lib/enhanced-github-sync.js +0 -220
  316. package/plugins/specweave-github/lib/enhanced-github-sync.ts +0 -322
  317. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +0 -134
  318. package/plugins/specweave-jira/lib/enhanced-jira-sync.ts +0 -196
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Update Recommender
3
+ *
4
+ * Generates recommendations for handling discrepancies and creates update patches.
5
+ *
6
+ * @module core/discrepancy/update-recommender
7
+ */
8
+ import { Logger } from '../../utils/logger.js';
9
+ import { Discrepancy, DiscrepancyRecommendation } from './detector.js';
10
+ /**
11
+ * An update patch for a spec file.
12
+ */
13
+ export interface UpdatePatch {
14
+ filePath: string;
15
+ lineNumber: number;
16
+ originalText: string;
17
+ newText: string;
18
+ reason: string;
19
+ }
20
+ /**
21
+ * Recommendation with optional patch.
22
+ */
23
+ export interface RecommendationResult {
24
+ discrepancyId: string;
25
+ recommendation: DiscrepancyRecommendation;
26
+ patch?: UpdatePatch;
27
+ explanation: string;
28
+ riskLevel: 'low' | 'medium' | 'high';
29
+ }
30
+ /**
31
+ * Configuration for the update recommender.
32
+ */
33
+ export interface UpdateRecommenderConfig {
34
+ /** Auto-update trivial changes */
35
+ autoUpdateTrivial?: boolean;
36
+ /** Generate patches for review-required changes */
37
+ generatePatches?: boolean;
38
+ }
39
+ /**
40
+ * Generates recommendations for handling discrepancies.
41
+ */
42
+ export declare class UpdateRecommender {
43
+ private readonly logger;
44
+ private readonly config;
45
+ constructor(options?: {
46
+ logger?: Logger;
47
+ config?: UpdateRecommenderConfig;
48
+ });
49
+ /**
50
+ * Generate recommendations for a list of discrepancies.
51
+ */
52
+ recommendAll(discrepancies: Discrepancy[]): RecommendationResult[];
53
+ /**
54
+ * Generate a recommendation for a single discrepancy.
55
+ */
56
+ recommend(discrepancy: Discrepancy): RecommendationResult;
57
+ /**
58
+ * Generate an update patch for a discrepancy.
59
+ */
60
+ generatePatch(discrepancy: Discrepancy): UpdatePatch | undefined;
61
+ private mapSeverityToRecommendation;
62
+ private generateExplanation;
63
+ private assessRisk;
64
+ private shouldGeneratePatch;
65
+ private generateApiRoutePatch;
66
+ private generateFunctionPatch;
67
+ private generateTypePatch;
68
+ private generateApiDocSection;
69
+ private generateFunctionDocSection;
70
+ private generateTypeDocSection;
71
+ /**
72
+ * Generate a summary report of recommendations.
73
+ */
74
+ generateReport(recommendations: RecommendationResult[]): string;
75
+ private groupBy;
76
+ }
77
+ //# sourceMappingURL=update-recommender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-recommender.d.ts","sourceRoot":"","sources":["../../../../src/core/discrepancy/update-recommender.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAuB,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE5F;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,yBAAyB,CAAC;IAC1C,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,kCAAkC;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mDAAmD;IACnD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;gBAE/C,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,uBAAuB,CAAA;KAAO;IAQ/E;;OAEG;IACH,YAAY,CAAC,aAAa,EAAE,WAAW,EAAE,GAAG,oBAAoB,EAAE;IAIlE;;OAEG;IACH,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,oBAAoB;IAoBzD;;OAEG;IACH,aAAa,CAAC,WAAW,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS;IAkBhE,OAAO,CAAC,2BAA2B;IAanC,OAAO,CAAC,mBAAmB;IAsC3B,OAAO,CAAC,UAAU;IA6BlB,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,qBAAqB;IAsC7B,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,0BAA0B;IAgBlC,OAAO,CAAC,sBAAsB;IAY9B;;OAEG;IACH,cAAc,CAAC,eAAe,EAAE,oBAAoB,EAAE,GAAG,MAAM;IA8D/D,OAAO,CAAC,OAAO;CAWhB"}
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Update Recommender
3
+ *
4
+ * Generates recommendations for handling discrepancies and creates update patches.
5
+ *
6
+ * @module core/discrepancy/update-recommender
7
+ */
8
+ import { consoleLogger } from '../../utils/logger.js';
9
+ /**
10
+ * Generates recommendations for handling discrepancies.
11
+ */
12
+ export class UpdateRecommender {
13
+ constructor(options = {}) {
14
+ this.logger = options.logger ?? consoleLogger;
15
+ this.config = {
16
+ autoUpdateTrivial: options.config?.autoUpdateTrivial ?? false,
17
+ generatePatches: options.config?.generatePatches ?? true,
18
+ };
19
+ }
20
+ /**
21
+ * Generate recommendations for a list of discrepancies.
22
+ */
23
+ recommendAll(discrepancies) {
24
+ return discrepancies.map(d => this.recommend(d));
25
+ }
26
+ /**
27
+ * Generate a recommendation for a single discrepancy.
28
+ */
29
+ recommend(discrepancy) {
30
+ const recommendation = this.mapSeverityToRecommendation(discrepancy.severity);
31
+ const explanation = this.generateExplanation(discrepancy, recommendation);
32
+ const riskLevel = this.assessRisk(discrepancy);
33
+ const result = {
34
+ discrepancyId: discrepancy.id,
35
+ recommendation,
36
+ explanation,
37
+ riskLevel,
38
+ };
39
+ // Generate patch if applicable
40
+ if (this.config.generatePatches && this.shouldGeneratePatch(discrepancy, recommendation)) {
41
+ result.patch = this.generatePatch(discrepancy);
42
+ }
43
+ return result;
44
+ }
45
+ /**
46
+ * Generate an update patch for a discrepancy.
47
+ */
48
+ generatePatch(discrepancy) {
49
+ // Can only generate patches for modifications and additions with spec files
50
+ if (!discrepancy.specPath && discrepancy.category !== 'added') {
51
+ return undefined;
52
+ }
53
+ switch (discrepancy.type) {
54
+ case 'api-route':
55
+ return this.generateApiRoutePatch(discrepancy);
56
+ case 'function-signature':
57
+ return this.generateFunctionPatch(discrepancy);
58
+ case 'type-definition':
59
+ return this.generateTypePatch(discrepancy);
60
+ default:
61
+ return undefined;
62
+ }
63
+ }
64
+ mapSeverityToRecommendation(severity) {
65
+ switch (severity) {
66
+ case 'trivial':
67
+ return 'auto-update';
68
+ case 'minor':
69
+ return 'review-required';
70
+ case 'major':
71
+ return 'notify';
72
+ case 'breaking':
73
+ return 'alert';
74
+ }
75
+ }
76
+ generateExplanation(discrepancy, recommendation) {
77
+ const parts = [];
78
+ // Category explanation
79
+ switch (discrepancy.category) {
80
+ case 'added':
81
+ parts.push(`New ${discrepancy.type.replace('-', ' ')} found in code that is not documented.`);
82
+ break;
83
+ case 'removed':
84
+ parts.push(`Documented ${discrepancy.type.replace('-', ' ')} no longer exists in code.`);
85
+ break;
86
+ case 'modified':
87
+ parts.push(`${discrepancy.type.replace('-', ' ')} has changed from documented version.`);
88
+ break;
89
+ }
90
+ // Code is source of truth reminder
91
+ parts.push('Code is the source of truth - specs should be updated to match.');
92
+ // Recommendation explanation
93
+ switch (recommendation) {
94
+ case 'auto-update':
95
+ parts.push('This is a trivial change that can be safely auto-updated.');
96
+ break;
97
+ case 'review-required':
98
+ parts.push('Please review the change before updating documentation.');
99
+ break;
100
+ case 'notify':
101
+ parts.push('This is a significant change that should be communicated to stakeholders.');
102
+ break;
103
+ case 'alert':
104
+ parts.push('⚠️ This is a breaking change! Consumers may be affected.');
105
+ break;
106
+ }
107
+ return parts.join(' ');
108
+ }
109
+ assessRisk(discrepancy) {
110
+ // Breaking changes are high risk
111
+ if (discrepancy.severity === 'breaking') {
112
+ return 'high';
113
+ }
114
+ // Removals are always at least medium risk
115
+ if (discrepancy.category === 'removed') {
116
+ return 'medium';
117
+ }
118
+ // API route changes are medium risk
119
+ if (discrepancy.type === 'api-route' && discrepancy.category === 'modified') {
120
+ return 'medium';
121
+ }
122
+ // Additions are low risk (new undocumented functionality)
123
+ if (discrepancy.category === 'added') {
124
+ return 'low';
125
+ }
126
+ // Minor modifications are low risk
127
+ if (discrepancy.severity === 'minor' || discrepancy.severity === 'trivial') {
128
+ return 'low';
129
+ }
130
+ return 'medium';
131
+ }
132
+ shouldGeneratePatch(discrepancy, recommendation) {
133
+ // Don't generate patches for alerts (need manual handling)
134
+ if (recommendation === 'alert') {
135
+ return false;
136
+ }
137
+ // Generate patches for auto-update and review-required
138
+ if (recommendation === 'auto-update' || recommendation === 'review-required') {
139
+ return true;
140
+ }
141
+ // Generate patches for notify if it's an addition
142
+ if (recommendation === 'notify' && discrepancy.category === 'added') {
143
+ return true;
144
+ }
145
+ return false;
146
+ }
147
+ generateApiRoutePatch(discrepancy) {
148
+ if (discrepancy.category === 'added') {
149
+ // Generate new documentation section
150
+ const method = discrepancy.codeValue.split(' ')[0];
151
+ const path = discrepancy.codeValue.split(' ')[1] || discrepancy.codeValue;
152
+ return {
153
+ filePath: discrepancy.codePath || 'docs/api.md',
154
+ lineNumber: 0, // Append to end
155
+ originalText: '',
156
+ newText: this.generateApiDocSection(method, path),
157
+ reason: 'Add documentation for new API route',
158
+ };
159
+ }
160
+ if (discrepancy.category === 'modified' && discrepancy.specPath) {
161
+ return {
162
+ filePath: discrepancy.specPath,
163
+ lineNumber: discrepancy.specLineNumber,
164
+ originalText: discrepancy.specValue,
165
+ newText: discrepancy.codeValue,
166
+ reason: discrepancy.description,
167
+ };
168
+ }
169
+ if (discrepancy.category === 'removed' && discrepancy.specPath) {
170
+ return {
171
+ filePath: discrepancy.specPath,
172
+ lineNumber: discrepancy.specLineNumber,
173
+ originalText: discrepancy.specValue,
174
+ newText: `~~${discrepancy.specValue}~~ *(DEPRECATED - removed from code)*`,
175
+ reason: 'Mark removed API route as deprecated',
176
+ };
177
+ }
178
+ return undefined;
179
+ }
180
+ generateFunctionPatch(discrepancy) {
181
+ if (discrepancy.category === 'added') {
182
+ return {
183
+ filePath: discrepancy.codePath || 'docs/api.md',
184
+ lineNumber: 0,
185
+ originalText: '',
186
+ newText: this.generateFunctionDocSection(discrepancy.codeValue),
187
+ reason: 'Add documentation for new function',
188
+ };
189
+ }
190
+ if (discrepancy.category === 'modified' && discrepancy.specPath) {
191
+ return {
192
+ filePath: discrepancy.specPath,
193
+ lineNumber: discrepancy.specLineNumber,
194
+ originalText: discrepancy.specValue,
195
+ newText: discrepancy.codeValue,
196
+ reason: discrepancy.description,
197
+ };
198
+ }
199
+ return undefined;
200
+ }
201
+ generateTypePatch(discrepancy) {
202
+ if (discrepancy.category === 'added') {
203
+ return {
204
+ filePath: discrepancy.codePath || 'docs/types.md',
205
+ lineNumber: 0,
206
+ originalText: '',
207
+ newText: this.generateTypeDocSection(discrepancy.codeValue),
208
+ reason: 'Add documentation for new type',
209
+ };
210
+ }
211
+ return undefined;
212
+ }
213
+ generateApiDocSection(method, path) {
214
+ return `
215
+ ## ${method} ${path}
216
+
217
+ *TODO: Add description*
218
+
219
+ ### Request
220
+
221
+ *TODO: Document request parameters*
222
+
223
+ ### Response
224
+
225
+ *TODO: Document response format*
226
+ `.trim();
227
+ }
228
+ generateFunctionDocSection(signature) {
229
+ return `
230
+ ### \`${signature}\`
231
+
232
+ *TODO: Add description*
233
+
234
+ #### Parameters
235
+
236
+ *TODO: Document parameters*
237
+
238
+ #### Returns
239
+
240
+ *TODO: Document return value*
241
+ `.trim();
242
+ }
243
+ generateTypeDocSection(typeValue) {
244
+ return `
245
+ ### ${typeValue}
246
+
247
+ *TODO: Add type description*
248
+
249
+ #### Properties
250
+
251
+ *TODO: Document properties*
252
+ `.trim();
253
+ }
254
+ /**
255
+ * Generate a summary report of recommendations.
256
+ */
257
+ generateReport(recommendations) {
258
+ const lines = ['# Discrepancy Recommendations Report', ''];
259
+ const byRecommendation = this.groupBy(recommendations, r => r.recommendation);
260
+ // Summary
261
+ lines.push('## Summary', '');
262
+ lines.push(`- Total discrepancies: ${recommendations.length}`);
263
+ lines.push(`- Auto-update: ${byRecommendation['auto-update']?.length ?? 0}`);
264
+ lines.push(`- Review required: ${byRecommendation['review-required']?.length ?? 0}`);
265
+ lines.push(`- Notify: ${byRecommendation['notify']?.length ?? 0}`);
266
+ lines.push(`- Alert: ${byRecommendation['alert']?.length ?? 0}`);
267
+ lines.push('');
268
+ // Alerts first (most important)
269
+ if (byRecommendation['alert']?.length) {
270
+ lines.push('## ⚠️ Alerts (Breaking Changes)', '');
271
+ for (const r of byRecommendation['alert']) {
272
+ lines.push(`### ${r.discrepancyId}`);
273
+ lines.push(r.explanation);
274
+ lines.push('');
275
+ }
276
+ }
277
+ // Notifications
278
+ if (byRecommendation['notify']?.length) {
279
+ lines.push('## 📢 Notifications (Major Changes)', '');
280
+ for (const r of byRecommendation['notify']) {
281
+ lines.push(`### ${r.discrepancyId}`);
282
+ lines.push(r.explanation);
283
+ if (r.patch) {
284
+ lines.push('');
285
+ lines.push('**Suggested update:**');
286
+ lines.push('```');
287
+ lines.push(r.patch.newText);
288
+ lines.push('```');
289
+ }
290
+ lines.push('');
291
+ }
292
+ }
293
+ // Review required
294
+ if (byRecommendation['review-required']?.length) {
295
+ lines.push('## 👀 Review Required (Minor Changes)', '');
296
+ for (const r of byRecommendation['review-required']) {
297
+ lines.push(`- **${r.discrepancyId}**: ${r.explanation}`);
298
+ }
299
+ lines.push('');
300
+ }
301
+ // Auto-update
302
+ if (byRecommendation['auto-update']?.length) {
303
+ lines.push('## ✅ Auto-Update (Trivial Changes)', '');
304
+ for (const r of byRecommendation['auto-update']) {
305
+ lines.push(`- **${r.discrepancyId}**: ${r.explanation}`);
306
+ }
307
+ lines.push('');
308
+ }
309
+ return lines.join('\n');
310
+ }
311
+ groupBy(items, keyFn) {
312
+ const result = {};
313
+ for (const item of items) {
314
+ const key = keyFn(item);
315
+ if (!result[key]) {
316
+ result[key] = [];
317
+ }
318
+ result[key].push(item);
319
+ }
320
+ return result;
321
+ }
322
+ }
323
+ //# sourceMappingURL=update-recommender.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-recommender.js","sourceRoot":"","sources":["../../../../src/core/discrepancy/update-recommender.ts"],"names":[],"mappings":"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"}
@@ -47,26 +47,23 @@ export declare class LivingDocsSync {
47
47
  */
48
48
  syncIncrement(incrementId: string, options?: SyncOptions): Promise<SyncResult>;
49
49
  /**
50
- * Get feature ID for increment (auto-generates for greenfield)
50
+ * Get feature ID for increment
51
51
  *
52
- * CRITICAL: Validates feature ID format matches increment type (greenfield vs brownfield)
53
- * - Greenfield (SpecWeave-native): FS-XXX (e.g., FS-031, FS-043)
54
- * - Brownfield (imported): FS-YY-MM-DD-name (e.g., FS-25-11-14-jira-epic)
55
- */
56
- private getFeatureIdForIncrement;
57
- /**
58
- * Update metadata.json with feature_id
52
+ * SIMPLIFIED (v0.29.0): Feature ID is derived from increment number
53
+ * No longer reads from metadata.json - derivation is the single source of truth
59
54
  *
60
- * CRITICAL FIX (2025-11-24): Write back generated feature_id to metadata.json
61
- * This ensures:
62
- * 1. feature_id persists across sessions
63
- * 2. Subsequent syncs reuse the same feature_id
64
- * 3. External tools (GitHub, JIRA, ADO) can find the feature
55
+ * CRITICAL FIX (2025-12-01): Internal features are DETERMINISTIC - no collision check!
56
+ * - Increment 0060 → ALWAYS FS-060, never anything else
57
+ * - Increment 0072 ALWAYS FS-072, never anything else
58
+ * - Sync is IDEMPOTENT: if FS-060 exists, UPDATE it (don't create FS-061)
65
59
  *
66
- * @param incrementId - Increment ID
67
- * @param featureId - Feature ID to write
60
+ * Collision detection is ONLY for EXTERNAL features (FS-XXXE) during imports,
61
+ * NOT for internal features derived from increment numbers.
62
+ *
63
+ * @see deriveFeatureId() in src/utils/feature-id-derivation.ts
64
+ * @see ADR-0140 for rationale
68
65
  */
69
- private updateMetadataFeatureId;
66
+ private getFeatureIdForIncrement;
70
67
  /**
71
68
  * Parse increment spec.md
72
69
  */
@@ -1 +1 @@
1
- {"version":3,"file":"living-docs-sync.d.ts","sourceRoot":"","sources":["../../../../src/core/living-docs/living-docs-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAK9D,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAkBpB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,uBAAuB,EAAE,CAAC;AAE5F,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAQlE;;;;;;;OAOG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;OAKG;YACW,mBAAmB;IASjC;;OAEG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA8KxF;;;;;;OAMG;YACW,wBAAwB;IA4DtC;;;;;;;;;;;OAWG;YACW,uBAAuB;IAiCrC;;OAEG;YACW,kBAAkB;IAmHhC;;;;OAIG;YACW,sBAAsB;IAkCpC;;OAEG;YACW,kBAAkB;IAmChC;;;;;;;OAOG;YACW,mBAAmB;IA8CjC;;;;;;OAMG;IACH;;;;;;;;;;;OAWG;YACW,mBAAmB;IA4HjC;;;;;;;;;OASG;YACW,YAAY;IA0F1B;;OAEG;YACW,UAAU;IAKxB;;OAEG;YACW,SAAS;IAKvB;;;;;;;;OAQG;YACW,4BAA4B;CAmB3C"}
1
+ {"version":3,"file":"living-docs-sync.d.ts","sourceRoot":"","sources":["../../../../src/core/living-docs/living-docs-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAO9D,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAkBpB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,uBAAuB,EAAE,CAAC;AAE5F,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAQlE;;;;;;;OAOG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;OAKG;YACW,mBAAmB;IASjC;;OAEG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA4JxF;;;;;;;;;;;;;;;;OAgBG;YACW,wBAAwB;IAUtC;;OAEG;YACW,kBAAkB;IAmHhC;;;;OAIG;YACW,sBAAsB;IAkCpC;;OAEG;YACW,kBAAkB;IAmChC;;;;;;;OAOG;YACW,mBAAmB;IA8CjC;;;;;;OAMG;IACH;;;;;;;;;;;OAWG;YACW,mBAAmB;IA4HjC;;;;;;;;;OASG;YACW,YAAY;IA0F1B;;OAEG;YACW,UAAU;IAKxB;;OAEG;YACW,SAAS;IAKvB;;;;;;;;OAQG;YACW,4BAA4B;CAmB3C"}
@@ -23,7 +23,9 @@ import { TaskProjectSpecificGenerator } from './task-project-specific-generator.
23
23
  import { consoleLogger } from '../../utils/logger.js';
24
24
  import { autoDetectProjectIdSync } from '../../utils/project-detection.js';
25
25
  import { getGitHubAuthFromProject } from '../../utils/auth-helpers.js';
26
- import { findNextAvailableInternalIdSync } from '../../utils/feature-id-collision.js';
26
+ // NOTE (2025-12-01): findNextAvailableInternalIdSync removed - internal features don't need collision check
27
+ // Collision detection is only for EXTERNAL features (FS-XXXE) imported from GitHub/JIRA/ADO
28
+ import { deriveFeatureId } from '../../utils/feature-id-derivation.js';
27
29
  // Import extracted helpers
28
30
  import { calculateUSStatus, extractUserStories, extractAcceptanceCriteria, generateFeatureFile, generateUserStoryFile, pathExists, readJson, ensureDir, findExistingUserStoryFile, cleanupDuplicateFiles, cleanupTempFiles, } from './sync-helpers/index.js';
29
31
  export class LivingDocsSync {
@@ -92,41 +94,22 @@ export class LivingDocsSync {
92
94
  }
93
95
  // Step 1: Build feature registry (auto-generates IDs for greenfield)
94
96
  await this.featureIdManager.buildRegistry();
95
- // Step 2: Get/assign feature ID
96
- // NEW (v0.23.0): Use explicitFeatureId from spec.md epic field if provided
97
+ // Step 2: Get feature ID (derived from increment number)
98
+ // SIMPLIFIED (v0.29.0): Feature ID is now always derived, not stored in metadata
97
99
  let featureId;
98
- if (options.explicitFeatureId) {
100
+ if (options.explicitFeatureId && /^FS-\d{3,}E?$/.test(options.explicitFeatureId)) {
101
+ // Allow explicit override for special cases (e.g., epic linking)
99
102
  featureId = options.explicitFeatureId;
100
- this.logger.log(`📎 Using explicit feature ID from spec.md: ${featureId}`);
103
+ this.logger.log(`📎 Using explicit feature ID: ${featureId}`);
101
104
  }
102
105
  else {
106
+ // Derive from increment number (the normal case)
103
107
  featureId = await this.getFeatureIdForIncrement(incrementId);
104
- this.logger.log(`🔄 Auto-generated feature ID: ${featureId}`);
105
- }
106
- // CRITICAL FIX (2025-11-26): Validate featureId to prevent "null" folder bug
107
- // If featureId is invalid (null, "null", empty, or not FS-XXX format), auto-generate
108
- if (!featureId || featureId === 'null' || !/^FS-\d{3,}E?$/.test(featureId)) {
109
- const invalidValue = featureId;
110
- // Extract increment number for auto-generation
111
- const match = incrementId.match(/^(\d{4})-/);
112
- if (match) {
113
- const baseNum = parseInt(match[1], 10);
114
- // CRITICAL FIX (2025-11-26): Check for FS-XXXE collision before using FS-XXX
115
- const specsPath = path.join(this.projectRoot, '.specweave/docs/internal/specs');
116
- const safeNum = findNextAvailableInternalIdSync(baseNum, specsPath, this.projectId, { logger: this.logger });
117
- featureId = `FS-${String(safeNum).padStart(3, '0')}`;
118
- this.logger.warn(`⚠️ Invalid feature ID "${invalidValue}" replaced with auto-generated: ${featureId}`);
119
- }
120
- else {
121
- throw new Error(`Cannot sync increment ${incrementId}: invalid feature ID "${invalidValue}" and unable to auto-generate`);
122
- }
108
+ this.logger.log(`🔄 Derived feature ID: ${featureId}`);
123
109
  }
124
110
  result.featureId = featureId;
125
- // CRITICAL FIX (2025-11-24): Write feature_id back to metadata.json
126
- // This ensures the generated feature_id persists and is reused on subsequent syncs
127
- if (!options.dryRun) {
128
- await this.updateMetadataFeatureId(incrementId, featureId);
129
- }
111
+ // NOTE (v0.29.0): No longer write feature_id to metadata.json
112
+ // Feature ID is derived from increment number - see ADR-0140
130
113
  this.logger.log(`📚 Syncing ${incrementId} → ${featureId}...`);
131
114
  // Step 3: Parse increment spec
132
115
  const parsed = await this.parseIncrementSpec(incrementId);
@@ -211,94 +194,30 @@ export class LivingDocsSync {
211
194
  }
212
195
  }
213
196
  /**
214
- * Get feature ID for increment (auto-generates for greenfield)
197
+ * Get feature ID for increment
215
198
  *
216
- * CRITICAL: Validates feature ID format matches increment type (greenfield vs brownfield)
217
- * - Greenfield (SpecWeave-native): FS-XXX (e.g., FS-031, FS-043)
218
- * - Brownfield (imported): FS-YY-MM-DD-name (e.g., FS-25-11-14-jira-epic)
219
- */
220
- async getFeatureIdForIncrement(incrementId) {
221
- // Extract increment number (e.g., "0040-name" → 40)
222
- const match = incrementId.match(/^(\d{4})-/);
223
- if (!match) {
224
- throw new Error(`Invalid increment ID format: ${incrementId}`);
225
- }
226
- const num = parseInt(match[1], 10);
227
- // Check if increment has explicit feature ID
228
- const metadataPath = path.join(this.projectRoot, '.specweave/increments', incrementId, 'metadata.json');
229
- if (await pathExists(metadataPath)) {
230
- const metadata = await readJson(metadataPath);
231
- // Check if brownfield (imported from external tool)
232
- const isBrownfield = metadata.imported === true || metadata.source === 'external';
233
- // CRITICAL (v0.26.2): Check for feature_id field (primary) or feature (legacy)
234
- // Bug fix: metadata.json uses "feature_id" not "feature"
235
- // See: Living docs sync bug report (2025-11-24)
236
- const featureId = metadata.feature_id || metadata.feature;
237
- if (featureId) {
238
- // Validate format matches increment type
239
- const isDateFormat = /^FS-\d{2}-\d{2}-\d{2}/.test(featureId);
240
- const isIncrementFormat = /^FS-\d{3,}$/.test(featureId);
241
- if (isBrownfield && isDateFormat) {
242
- // ✅ Brownfield with correct date format
243
- return featureId;
244
- }
245
- else if (!isBrownfield && isIncrementFormat) {
246
- // ✅ Greenfield with correct increment format (3+ digits)
247
- return featureId;
248
- }
249
- else {
250
- // ⚠️ Format mismatch - log warning and auto-generate correct format
251
- this.logger.warn(`⚠️ Feature ID format mismatch for ${incrementId}:`);
252
- this.logger.warn(` Found: ${featureId}`);
253
- this.logger.warn(` Expected: ${isBrownfield ? 'FS-YY-MM-DD-name (brownfield)' : 'FS-XXX (greenfield, 3+ digits)'}`);
254
- this.logger.warn(` Auto-generating correct format...`);
255
- // Fall through to auto-generation
256
- }
257
- }
258
- }
259
- // Auto-generate for greenfield: FS-040, FS-041, etc.
260
- // CRITICAL FIX (2025-11-26): Check for FS-XXXE collision before using FS-XXX
261
- const specsPath = path.join(this.projectRoot, '.specweave/docs/internal/specs');
262
- const safeNum = findNextAvailableInternalIdSync(num, specsPath, this.projectId, { logger: this.logger });
263
- const autoGenId = `FS-${String(safeNum).padStart(3, '0')}`;
264
- this.logger.log(` 📝 Generated feature ID: ${autoGenId}`);
265
- return autoGenId;
266
- }
267
- /**
268
- * Update metadata.json with feature_id
199
+ * SIMPLIFIED (v0.29.0): Feature ID is derived from increment number
200
+ * No longer reads from metadata.json - derivation is the single source of truth
269
201
  *
270
- * CRITICAL FIX (2025-11-24): Write back generated feature_id to metadata.json
271
- * This ensures:
272
- * 1. feature_id persists across sessions
273
- * 2. Subsequent syncs reuse the same feature_id
274
- * 3. External tools (GitHub, JIRA, ADO) can find the feature
202
+ * CRITICAL FIX (2025-12-01): Internal features are DETERMINISTIC - no collision check!
203
+ * - Increment 0060 → ALWAYS FS-060, never anything else
204
+ * - Increment 0072 ALWAYS FS-072, never anything else
205
+ * - Sync is IDEMPOTENT: if FS-060 exists, UPDATE it (don't create FS-061)
275
206
  *
276
- * @param incrementId - Increment ID
277
- * @param featureId - Feature ID to write
207
+ * Collision detection is ONLY for EXTERNAL features (FS-XXXE) during imports,
208
+ * NOT for internal features derived from increment numbers.
209
+ *
210
+ * @see deriveFeatureId() in src/utils/feature-id-derivation.ts
211
+ * @see ADR-0140 for rationale
278
212
  */
279
- async updateMetadataFeatureId(incrementId, featureId) {
280
- const metadataPath = path.join(this.projectRoot, '.specweave/increments', incrementId, 'metadata.json');
281
- try {
282
- if (!await pathExists(metadataPath)) {
283
- this.logger.warn(` ⚠️ metadata.json not found for ${incrementId}, skipping feature_id update`);
284
- return;
285
- }
286
- const metadata = await readJson(metadataPath);
287
- // Only update if feature_id is null/undefined or different
288
- if (metadata.feature_id !== featureId) {
289
- metadata.feature_id = featureId;
290
- // Atomic write: temp file → rename
291
- const tempPath = `${metadataPath}.tmp`;
292
- await fs.writeFile(tempPath, JSON.stringify(metadata, null, 2), 'utf-8');
293
- await fs.rename(tempPath, metadataPath);
294
- this.logger.log(` ✅ Updated metadata.json with feature_id: ${featureId}`);
295
- }
296
- }
297
- catch (error) {
298
- // Non-fatal - log warning but continue with sync
299
- this.logger.warn(` ⚠️ Failed to update metadata.json with feature_id: ${error}`);
300
- }
213
+ async getFeatureIdForIncrement(incrementId) {
214
+ // Derive feature ID directly from increment number (e.g., "0081-name" "FS-081")
215
+ // NO collision checking - internal feature IDs are deterministic and unique by design
216
+ return deriveFeatureId(incrementId);
301
217
  }
218
+ // NOTE (v0.29.0): updateMetadataFeatureId() was REMOVED
219
+ // Feature ID is now derived from increment number, not stored in metadata
220
+ // See ADR-0140 for rationale
302
221
  /**
303
222
  * Parse increment spec.md
304
223
  */