@taskforcehq/taskforce 0.3.311 → 0.3.313

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 (454) hide show
  1. package/README.md +40 -2
  2. package/dist/TaskforceCore.js +1 -1
  3. package/dist/components/features/InitiativesModule.js +1 -1
  4. package/dist/components/features/TaskSettings.js +1 -1
  5. package/dist/components/views/StandaloneLayout.js +3 -6
  6. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +1 -2
  7. package/dist/components/views/standalone/modals/AccountHubModal.js +2 -3
  8. package/dist/components/views/standalone/modals/HelpModal.js +15 -2
  9. package/dist/hooks/useFilterSort.js +1 -1
  10. package/dist/hooks/useTaskforce.js +2 -2
  11. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +5 -2
  12. package/dist/ui/assets/{AgentsModule-D8u9P5dZ.js → AgentsModule-BULqIo8e.js} +1 -1
  13. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-CsUauFA2.js → AnnotatedAttachmentWorkspace-DHr4rHK9.js} +1 -1
  14. package/dist/ui/assets/{ContextAttachmentManager-DbQvDHFz.js → ContextAttachmentManager-DoMe2OpY.js} +1 -1
  15. package/dist/ui/assets/{DocumentWorkspace-BmTkN1A8.js → DocumentWorkspace-DumBWhvb.js} +1 -1
  16. package/dist/ui/assets/{EntityActivityTimeline-BXHTM-Nh.js → EntityActivityTimeline-sHk8Nzbq.js} +1 -1
  17. package/dist/ui/assets/{InitiativesModule-D33RyXx0.js → InitiativesModule-3X9UNzHh.js} +1 -1
  18. package/dist/ui/assets/{PlansPage-D0RXlT9_.js → PlansPage-C3A3EYaV.js} +1 -1
  19. package/dist/ui/assets/{TaskContextUpload-Vsg67lpR.js → TaskContextUpload-C8G7pWkX.js} +1 -1
  20. package/dist/ui/assets/{TaskSettings-DshLw7_d.js → TaskSettings-DM1o9Wla.js} +2 -2
  21. package/dist/ui/assets/{WorkflowsModule-CLM-1BnF.js → WorkflowsModule-CW0crzvD.js} +1 -1
  22. package/dist/ui/assets/documentReferences-BZB3nMRb.js +1 -0
  23. package/dist/ui/assets/index-BWFQ9mGn.js +5 -0
  24. package/dist/ui/assets/{index-DOR9PRqt.css → index-DcK_8tuE.css} +1 -1
  25. package/dist/ui/index.html +2 -2
  26. package/package.json +12 -4
  27. package/scripts/postinstall.js +4 -3
  28. package/dist/TaskforceCore.CategoryFallback.test.d.ts +0 -1
  29. package/dist/TaskforceCore.CategoryFallback.test.js +0 -101
  30. package/dist/TaskforceCore.filter-persistence.test.d.ts +0 -1
  31. package/dist/TaskforceCore.filter-persistence.test.js +0 -402
  32. package/dist/TaskforceCore.routing.test.d.ts +0 -1
  33. package/dist/TaskforceCore.routing.test.js +0 -311
  34. package/dist/TaskforceCore.test.d.ts +0 -1
  35. package/dist/TaskforceCore.test.js +0 -7910
  36. package/dist/TaskforceWidget.test.d.ts +0 -1
  37. package/dist/TaskforceWidget.test.js +0 -92
  38. package/dist/UnsavedChangesRelaxed.test.d.ts +0 -1
  39. package/dist/UnsavedChangesRelaxed.test.js +0 -16
  40. package/dist/__tests__/localizationCatalogs.test.d.ts +0 -1
  41. package/dist/__tests__/localizationCatalogs.test.js +0 -54
  42. package/dist/__tests__/workflowSimulation.test.d.ts +0 -1
  43. package/dist/__tests__/workflowSimulation.test.js +0 -113
  44. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.test.d.ts +0 -1
  45. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.test.js +0 -278
  46. package/dist/annotatedAttachments/payload.test.d.ts +0 -1
  47. package/dist/annotatedAttachments/payload.test.js +0 -67
  48. package/dist/compat/workspaceSyncCompat.test.d.ts +0 -1
  49. package/dist/compat/workspaceSyncCompat.test.js +0 -124
  50. package/dist/components/features/AgentsModule.test.d.ts +0 -1
  51. package/dist/components/features/AgentsModule.test.js +0 -601
  52. package/dist/components/features/AnnotatedAttachmentWorkspace.test.d.ts +0 -1
  53. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +0 -2883
  54. package/dist/components/features/DocumentWorkspace.test.d.ts +0 -1
  55. package/dist/components/features/DocumentWorkspace.test.js +0 -1439
  56. package/dist/components/features/TaskSettings.test.d.ts +0 -1
  57. package/dist/components/features/TaskSettings.test.js +0 -1674
  58. package/dist/components/features/settings/TaxonomyLibraryManager.test.d.ts +0 -1
  59. package/dist/components/features/settings/TaxonomyLibraryManager.test.js +0 -141
  60. package/dist/components/task/TaskActionHeader.test.d.ts +0 -1
  61. package/dist/components/task/TaskActionHeader.test.js +0 -91
  62. package/dist/components/task/TaskCard.test.d.ts +0 -1
  63. package/dist/components/task/TaskCard.test.js +0 -513
  64. package/dist/components/task/TaskContextUpload.test.d.ts +0 -1
  65. package/dist/components/task/TaskContextUpload.test.js +0 -506
  66. package/dist/components/task/TaskForm.test.d.ts +0 -1
  67. package/dist/components/task/TaskForm.test.js +0 -946
  68. package/dist/components/task/TaskKanban.empty-column-drop.test.d.ts +0 -1
  69. package/dist/components/task/TaskKanban.empty-column-drop.test.js +0 -66
  70. package/dist/components/task/TaskKanban.planning-drop.test.d.ts +0 -1
  71. package/dist/components/task/TaskKanban.planning-drop.test.js +0 -93
  72. package/dist/components/task/TaskKanban.test.d.ts +0 -1
  73. package/dist/components/task/TaskKanban.test.js +0 -99
  74. package/dist/components/task/TaskList.test.d.ts +0 -1
  75. package/dist/components/task/TaskList.test.js +0 -473
  76. package/dist/components/task/TaskStatusActions.test.d.ts +0 -1
  77. package/dist/components/task/TaskStatusActions.test.js +0 -54
  78. package/dist/components/task/taskKanbanDropRules.test.d.ts +0 -1
  79. package/dist/components/task/taskKanbanDropRules.test.js +0 -114
  80. package/dist/components/ui/LevelSelector.test.d.ts +0 -1
  81. package/dist/components/ui/LevelSelector.test.js +0 -55
  82. package/dist/components/ui/Markdown.test.d.ts +0 -1
  83. package/dist/components/ui/Markdown.test.js +0 -38
  84. package/dist/components/ui/MultiSelectFilter.test.d.ts +0 -1
  85. package/dist/components/ui/MultiSelectFilter.test.js +0 -74
  86. package/dist/components/ui/TaxonomyDropdown.test.d.ts +0 -1
  87. package/dist/components/ui/TaxonomyDropdown.test.js +0 -130
  88. package/dist/components/ui/TopNoticeLayer.test.d.ts +0 -1
  89. package/dist/components/ui/TopNoticeLayer.test.js +0 -25
  90. package/dist/components/views/AppShellHeader.test.d.ts +0 -1
  91. package/dist/components/views/AppShellHeader.test.js +0 -42
  92. package/dist/components/views/PlanComparisonPage.test.d.ts +0 -1
  93. package/dist/components/views/PlanComparisonPage.test.js +0 -758
  94. package/dist/components/views/PlansPage.test.d.ts +0 -1
  95. package/dist/components/views/PlansPage.test.js +0 -710
  96. package/dist/components/views/chrome/WorkspaceChromeSections.test.d.ts +0 -1
  97. package/dist/components/views/chrome/WorkspaceChromeSections.test.js +0 -54
  98. package/dist/components/views/chrome/workspaceChrome.test.d.ts +0 -1
  99. package/dist/components/views/chrome/workspaceChrome.test.js +0 -52
  100. package/dist/components/views/panels/DocumentFilterToolbar.test.d.ts +0 -1
  101. package/dist/components/views/panels/DocumentFilterToolbar.test.js +0 -19
  102. package/dist/components/views/panels/FilterToolbar.test.d.ts +0 -1
  103. package/dist/components/views/panels/FilterToolbar.test.js +0 -47
  104. package/dist/components/views/panels/PlanningDrawer.test.d.ts +0 -1
  105. package/dist/components/views/panels/PlanningDrawer.test.js +0 -529
  106. package/dist/components/views/planningScope.test.d.ts +0 -1
  107. package/dist/components/views/planningScope.test.js +0 -60
  108. package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +0 -1
  109. package/dist/components/views/standalone/modals/AccountHubModal.test.js +0 -72
  110. package/dist/components/views/standalone/modals/HelpModal.test.d.ts +0 -1
  111. package/dist/components/views/standalone/modals/HelpModal.test.js +0 -23
  112. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +0 -1
  113. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +0 -24
  114. package/dist/components/views/teamManagementAccess.test.d.ts +0 -1
  115. package/dist/components/views/teamManagementAccess.test.js +0 -44
  116. package/dist/config/deployment.test.d.ts +0 -1
  117. package/dist/config/deployment.test.js +0 -90
  118. package/dist/config/envSchema.test.d.ts +0 -1
  119. package/dist/config/envSchema.test.js +0 -125
  120. package/dist/core/AiProfileService.test.d.ts +0 -1
  121. package/dist/core/AiProfileService.test.js +0 -184
  122. package/dist/core/AiProfiles.test.d.ts +0 -1
  123. package/dist/core/AiProfiles.test.js +0 -1873
  124. package/dist/core/AuthTokenService.test.d.ts +0 -1
  125. package/dist/core/AuthTokenService.test.js +0 -78
  126. package/dist/core/BulkUpdateFieldsPreview.test.d.ts +0 -1
  127. package/dist/core/BulkUpdateFieldsPreview.test.js +0 -62
  128. package/dist/core/CompletedAt.test.d.ts +0 -1
  129. package/dist/core/CompletedAt.test.js +0 -44
  130. package/dist/core/CreatedBy.test.d.ts +0 -1
  131. package/dist/core/CreatedBy.test.js +0 -188
  132. package/dist/core/DeletedTaskTrash.test.d.ts +0 -1
  133. package/dist/core/DeletedTaskTrash.test.js +0 -104
  134. package/dist/core/EntitlementsPolicy.test.d.ts +0 -1
  135. package/dist/core/EntitlementsPolicy.test.js +0 -280
  136. package/dist/core/GlobalSettingsService.test.d.ts +0 -1
  137. package/dist/core/GlobalSettingsService.test.js +0 -45
  138. package/dist/core/JsonBackupRuntime.test.d.ts +0 -1
  139. package/dist/core/JsonBackupRuntime.test.js +0 -155
  140. package/dist/core/PlanFeatureCatalog.test.d.ts +0 -1
  141. package/dist/core/PlanFeatureCatalog.test.js +0 -139
  142. package/dist/core/PlanVersionPolicy.test.d.ts +0 -1
  143. package/dist/core/PlanVersionPolicy.test.js +0 -236
  144. package/dist/core/PlanningEntities.test.d.ts +0 -1
  145. package/dist/core/PlanningEntities.test.js +0 -509
  146. package/dist/core/ScheduleFields.test.d.ts +0 -1
  147. package/dist/core/ScheduleFields.test.js +0 -193
  148. package/dist/core/SetupState.test.d.ts +0 -1
  149. package/dist/core/SetupState.test.js +0 -113
  150. package/dist/core/SignupMonetizationSettings.test.d.ts +0 -1
  151. package/dist/core/SignupMonetizationSettings.test.js +0 -515
  152. package/dist/core/Stats.test.d.ts +0 -1
  153. package/dist/core/Stats.test.js +0 -36
  154. package/dist/core/SystemAdmin.test.d.ts +0 -1
  155. package/dist/core/SystemAdmin.test.js +0 -1106
  156. package/dist/core/TaskActivitySync.test.d.ts +0 -1
  157. package/dist/core/TaskActivitySync.test.js +0 -90
  158. package/dist/core/TaskAttachmentsCanonical.test.d.ts +0 -1
  159. package/dist/core/TaskAttachmentsCanonical.test.js +0 -356
  160. package/dist/core/TaskAuditTimeline.test.d.ts +0 -1
  161. package/dist/core/TaskAuditTimeline.test.js +0 -319
  162. package/dist/core/TaskTaxonomyValidation.test.d.ts +0 -1
  163. package/dist/core/TaskTaxonomyValidation.test.js +0 -440
  164. package/dist/core/Taskforce.ids.test.d.ts +0 -1
  165. package/dist/core/Taskforce.ids.test.js +0 -611
  166. package/dist/core/Taskforce.listTasksSlim.test.d.ts +0 -1
  167. package/dist/core/Taskforce.listTasksSlim.test.js +0 -91
  168. package/dist/core/Taskforce.mcpAuth.test.d.ts +0 -1
  169. package/dist/core/Taskforce.mcpAuth.test.js +0 -450
  170. package/dist/core/Taskforce.realtimeMutation.test.d.ts +0 -1
  171. package/dist/core/Taskforce.realtimeMutation.test.js +0 -131
  172. package/dist/core/UserProfileAvatarDrafts.test.d.ts +0 -1
  173. package/dist/core/UserProfileAvatarDrafts.test.js +0 -138
  174. package/dist/core/WorkspaceDelete.test.d.ts +0 -1
  175. package/dist/core/WorkspaceDelete.test.js +0 -159
  176. package/dist/core/WorkspacePermissions.test.d.ts +0 -1
  177. package/dist/core/WorkspacePermissions.test.js +0 -270
  178. package/dist/core/WorkspacePlanMode.test.d.ts +0 -1
  179. package/dist/core/WorkspacePlanMode.test.js +0 -383
  180. package/dist/core/WorkspaceSyncSettings.test.d.ts +0 -1
  181. package/dist/core/WorkspaceSyncSettings.test.js +0 -65
  182. package/dist/core/__tests__/TaskAttachmentsCanonical.test.d.ts +0 -1
  183. package/dist/core/__tests__/TaskAttachmentsCanonical.test.js +0 -212
  184. package/dist/documentReviews/DocumentReviewSessionStore.test.d.ts +0 -1
  185. package/dist/documentReviews/DocumentReviewSessionStore.test.js +0 -127
  186. package/dist/hooks/authGuardPolicy.test.d.ts +0 -1
  187. package/dist/hooks/authGuardPolicy.test.js +0 -76
  188. package/dist/hooks/bootstrapTimeouts.test.d.ts +0 -1
  189. package/dist/hooks/bootstrapTimeouts.test.js +0 -12
  190. package/dist/hooks/sync/controlPlane.test.d.ts +0 -1
  191. package/dist/hooks/sync/controlPlane.test.js +0 -121
  192. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +0 -1
  193. package/dist/hooks/sync/lifecyclePolicy.test.js +0 -124
  194. package/dist/hooks/sync/orchestratorShared.test.d.ts +0 -1
  195. package/dist/hooks/sync/orchestratorShared.test.js +0 -98
  196. package/dist/hooks/sync/pullLifecycle.test.d.ts +0 -1
  197. package/dist/hooks/sync/pullLifecycle.test.js +0 -80
  198. package/dist/hooks/sync/recovery.test.d.ts +0 -1
  199. package/dist/hooks/sync/recovery.test.js +0 -292
  200. package/dist/hooks/sync/transfers.test.d.ts +0 -1
  201. package/dist/hooks/sync/transfers.test.js +0 -928
  202. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +0 -1
  203. package/dist/hooks/sync/useRecentSyncEvents.test.js +0 -124
  204. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +0 -1
  205. package/dist/hooks/sync/useSyncStatusActions.test.js +0 -133
  206. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +0 -1
  207. package/dist/hooks/sync/useSyncStatusControls.test.js +0 -88
  208. package/dist/hooks/useFilterSort.test.d.ts +0 -1
  209. package/dist/hooks/useFilterSort.test.js +0 -419
  210. package/dist/hooks/useRealtimeSync.test.d.ts +0 -1
  211. package/dist/hooks/useRealtimeSync.test.js +0 -311
  212. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.d.ts +0 -1
  213. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +0 -54
  214. package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +0 -1
  215. package/dist/hooks/useSyncOrchestrator.context-assets.test.js +0 -228
  216. package/dist/hooks/useSyncOrchestrator.retry-closure.test.d.ts +0 -1
  217. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +0 -2027
  218. package/dist/hooks/useTaskData.test.d.ts +0 -1
  219. package/dist/hooks/useTaskData.test.js +0 -291
  220. package/dist/hooks/useTaskMutations.test.d.ts +0 -1
  221. package/dist/hooks/useTaskMutations.test.js +0 -256
  222. package/dist/hooks/useTaskforce.contract.test.d.ts +0 -1
  223. package/dist/hooks/useTaskforce.contract.test.js +0 -7
  224. package/dist/hooks/useTaskforce.priority-filters.test.d.ts +0 -1
  225. package/dist/hooks/useTaskforce.priority-filters.test.js +0 -28
  226. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +0 -1
  227. package/dist/hooks/useTaskforce.runtime-routing.test.js +0 -246
  228. package/dist/hooks/useTaskforce.sync-behavior.test.d.ts +0 -1
  229. package/dist/hooks/useTaskforce.sync-behavior.test.js +0 -3644
  230. package/dist/hooks/useWorkspaceSyncController.test.d.ts +0 -1
  231. package/dist/hooks/useWorkspaceSyncController.test.js +0 -192
  232. package/dist/hooks/workspace/useTaskforceApiRouting.test.d.ts +0 -1
  233. package/dist/hooks/workspace/useTaskforceApiRouting.test.js +0 -77
  234. package/dist/hooks/workspace/workspaceLocalState.test.d.ts +0 -1
  235. package/dist/hooks/workspace/workspaceLocalState.test.js +0 -59
  236. package/dist/localization/localizationCatalogs.test.d.ts +0 -1
  237. package/dist/localization/localizationCatalogs.test.js +0 -37
  238. package/dist/mcp/aiProfileBootstrap.test.d.ts +0 -1
  239. package/dist/mcp/aiProfileBootstrap.test.js +0 -68
  240. package/dist/mcp/canonicalAssetHelpers.test.d.ts +0 -1
  241. package/dist/mcp/canonicalAssetHelpers.test.js +0 -67
  242. package/dist/mcp/clientIntegrations.test.d.ts +0 -1
  243. package/dist/mcp/clientIntegrations.test.js +0 -219
  244. package/dist/mcp/optionResolution.test.d.ts +0 -1
  245. package/dist/mcp/optionResolution.test.js +0 -43
  246. package/dist/mcp/runtime.test.d.ts +0 -1
  247. package/dist/mcp/runtime.test.js +0 -5211
  248. package/dist/mcp/structuredCommentValidation.test.d.ts +0 -1
  249. package/dist/mcp/structuredCommentValidation.test.js +0 -31
  250. package/dist/mcp/structuredDocValidation.test.d.ts +0 -1
  251. package/dist/mcp/structuredDocValidation.test.js +0 -35
  252. package/dist/mcp/toolRegistry.test.d.ts +0 -1
  253. package/dist/mcp/toolRegistry.test.js +0 -34
  254. package/dist/migrations/billingSchemaParity.test.d.ts +0 -1
  255. package/dist/migrations/billingSchemaParity.test.js +0 -160
  256. package/dist/migrations/taskSchemaMigrations.test.d.ts +0 -1
  257. package/dist/migrations/taskSchemaMigrations.test.js +0 -225
  258. package/dist/qaOpen.test.d.ts +0 -1
  259. package/dist/qaOpen.test.js +0 -56
  260. package/dist/releaseVersion.test.d.ts +0 -1
  261. package/dist/releaseVersion.test.js +0 -32
  262. package/dist/runtime/appGates.test.d.ts +0 -1
  263. package/dist/runtime/appGates.test.js +0 -42
  264. package/dist/server/aiProfileAvatarAssets.test.d.ts +0 -1
  265. package/dist/server/aiProfileAvatarAssets.test.js +0 -213
  266. package/dist/server/annotatedAttachmentsRoutes.test.d.ts +0 -1
  267. package/dist/server/annotatedAttachmentsRoutes.test.js +0 -532
  268. package/dist/server/auth.test.d.ts +0 -1
  269. package/dist/server/auth.test.js +0 -131
  270. package/dist/server/buildInfo.test.d.ts +0 -1
  271. package/dist/server/buildInfo.test.js +0 -29
  272. package/dist/server/cors.test.d.ts +0 -1
  273. package/dist/server/cors.test.js +0 -77
  274. package/dist/server/documentReviewRoutes.test.d.ts +0 -1
  275. package/dist/server/documentReviewRoutes.test.js +0 -190
  276. package/dist/server/email.test.d.ts +0 -1
  277. package/dist/server/email.test.js +0 -113
  278. package/dist/server/index.cookieProxy.test.d.ts +0 -1
  279. package/dist/server/index.cookieProxy.test.js +0 -67
  280. package/dist/server/index.rateLimit.test.d.ts +0 -1
  281. package/dist/server/index.rateLimit.test.js +0 -22
  282. package/dist/server/index.test.d.ts +0 -1
  283. package/dist/server/index.test.js +0 -283
  284. package/dist/server/rateLimit.test.d.ts +0 -1
  285. package/dist/server/rateLimit.test.js +0 -107
  286. package/dist/server/realtimeSync.auth.test.d.ts +0 -1
  287. package/dist/server/realtimeSync.auth.test.js +0 -62
  288. package/dist/server/realtimeSync.replay.test.d.ts +0 -1
  289. package/dist/server/realtimeSync.replay.test.js +0 -192
  290. package/dist/server/realtimeSync.workspaceIsolation.test.d.ts +0 -1
  291. package/dist/server/realtimeSync.workspaceIsolation.test.js +0 -101
  292. package/dist/server/routes/billing.test.d.ts +0 -1
  293. package/dist/server/routes/billing.test.js +0 -3515
  294. package/dist/server/routes/shared.test.d.ts +0 -1
  295. package/dist/server/routes/shared.test.js +0 -32
  296. package/dist/server/routes/sync.integration.test.d.ts +0 -1
  297. package/dist/server/routes/sync.integration.test.js +0 -2932
  298. package/dist/server/routes/syncAuxRoutes.test.d.ts +0 -1
  299. package/dist/server/routes/syncAuxRoutes.test.js +0 -158
  300. package/dist/server/routes/syncRouteGuards.test.d.ts +0 -1
  301. package/dist/server/routes/syncRouteGuards.test.js +0 -94
  302. package/dist/server/routes.test.d.ts +0 -1
  303. package/dist/server/routes.test.js +0 -15612
  304. package/dist/server/runtimeMode.test.d.ts +0 -1
  305. package/dist/server/runtimeMode.test.js +0 -26
  306. package/dist/server/securityHeaders.test.d.ts +0 -1
  307. package/dist/server/securityHeaders.test.js +0 -70
  308. package/dist/services/templateEngine.test.d.ts +0 -1
  309. package/dist/services/templateEngine.test.js +0 -150
  310. package/dist/services/templates.test.d.ts +0 -1
  311. package/dist/services/templates.test.js +0 -45
  312. package/dist/services/workflowDocumentFixtures.test.d.ts +0 -1
  313. package/dist/services/workflowDocumentFixtures.test.js +0 -78
  314. package/dist/services/workflowExportMatrix.test.d.ts +0 -1
  315. package/dist/services/workflowExportMatrix.test.js +0 -113
  316. package/dist/services/workflowExportSnapshots.test.d.ts +0 -1
  317. package/dist/services/workflowExportSnapshots.test.js +0 -30
  318. package/dist/services/workflowGeneration.test.d.ts +0 -1
  319. package/dist/services/workflowGeneration.test.js +0 -12
  320. package/dist/services/workflowSimulation.test.d.ts +0 -1
  321. package/dist/services/workflowSimulation.test.js +0 -113
  322. package/dist/services/workflowV2Policy.test.d.ts +0 -1
  323. package/dist/services/workflowV2Policy.test.js +0 -80
  324. package/dist/shared/customTaxonomyLibrary.test.d.ts +0 -1
  325. package/dist/shared/customTaxonomyLibrary.test.js +0 -20
  326. package/dist/shared/passwordPolicy.test.d.ts +0 -1
  327. package/dist/shared/passwordPolicy.test.js +0 -34
  328. package/dist/shared/runtimeContract.test.d.ts +0 -1
  329. package/dist/shared/runtimeContract.test.js +0 -23
  330. package/dist/shared/taxonomyLibrary.test.d.ts +0 -1
  331. package/dist/shared/taxonomyLibrary.test.js +0 -26
  332. package/dist/shared/taxonomyState.test.d.ts +0 -1
  333. package/dist/shared/taxonomyState.test.js +0 -71
  334. package/dist/soak/preflight.cli.d.ts +0 -1
  335. package/dist/soak/preflight.cli.js +0 -28
  336. package/dist/soak/preflight.d.ts +0 -26
  337. package/dist/soak/preflight.js +0 -113
  338. package/dist/soak/preflight.test.d.ts +0 -1
  339. package/dist/soak/preflight.test.js +0 -39
  340. package/dist/storage/objectStorageClient.test.d.ts +0 -1
  341. package/dist/storage/objectStorageClient.test.js +0 -82
  342. package/dist/storage/objectStorageConfig.test.d.ts +0 -1
  343. package/dist/storage/objectStorageConfig.test.js +0 -50
  344. package/dist/storage/postgresAdapter.test.d.ts +0 -1
  345. package/dist/storage/postgresAdapter.test.js +0 -29
  346. package/dist/storage/workspaceAssetStore.test.d.ts +0 -1
  347. package/dist/storage/workspaceAssetStore.test.js +0 -588
  348. package/dist/sync/cloudSyncApi.test.d.ts +0 -1
  349. package/dist/sync/cloudSyncApi.test.js +0 -111
  350. package/dist/sync/collaborationSyncPayload.test.d.ts +0 -1
  351. package/dist/sync/collaborationSyncPayload.test.js +0 -125
  352. package/dist/sync/contentSyncPayload.test.d.ts +0 -1
  353. package/dist/sync/contentSyncPayload.test.js +0 -143
  354. package/dist/sync/encryptionService.test.d.ts +0 -1
  355. package/dist/sync/encryptionService.test.js +0 -52
  356. package/dist/sync/planningSyncPayload.test.d.ts +0 -1
  357. package/dist/sync/planningSyncPayload.test.js +0 -92
  358. package/dist/sync/realtimeFeature.test.d.ts +0 -1
  359. package/dist/sync/realtimeFeature.test.js +0 -30
  360. package/dist/sync/realtimeReconcile.test.d.ts +0 -1
  361. package/dist/sync/realtimeReconcile.test.js +0 -43
  362. package/dist/sync/syncApplyHandlers.test.d.ts +0 -1
  363. package/dist/sync/syncApplyHandlers.test.js +0 -626
  364. package/dist/sync/syncService.test.d.ts +0 -1
  365. package/dist/sync/syncService.test.js +0 -903
  366. package/dist/sync/taskSyncPayload.test.d.ts +0 -1
  367. package/dist/sync/taskSyncPayload.test.js +0 -54
  368. package/dist/sync/workspacePullFeed.test.d.ts +0 -1
  369. package/dist/sync/workspacePullFeed.test.js +0 -438
  370. package/dist/sync/workspaceSyncModel.test.d.ts +0 -1
  371. package/dist/sync/workspaceSyncModel.test.js +0 -1442
  372. package/dist/test/setup.d.ts +0 -1
  373. package/dist/test/setup.js +0 -15
  374. package/dist/ui/assets/documentReferences-BPWp6ce0.js +0 -1
  375. package/dist/ui/assets/index-zvbo2Jse.js +0 -5
  376. package/dist/utils/accountProfileSummaryCache.test.d.ts +0 -1
  377. package/dist/utils/accountProfileSummaryCache.test.js +0 -63
  378. package/dist/utils/annotatedAttachments.test.d.ts +0 -1
  379. package/dist/utils/annotatedAttachments.test.js +0 -108
  380. package/dist/utils/assignees.test.d.ts +0 -1
  381. package/dist/utils/assignees.test.js +0 -25
  382. package/dist/utils/avatarUpload.test.d.ts +0 -1
  383. package/dist/utils/avatarUpload.test.js +0 -40
  384. package/dist/utils/canonicalAssetKind.test.d.ts +0 -1
  385. package/dist/utils/canonicalAssetKind.test.js +0 -24
  386. package/dist/utils/coalescedRefresh.test.d.ts +0 -1
  387. package/dist/utils/coalescedRefresh.test.js +0 -30
  388. package/dist/utils/constants.icons.test.d.ts +0 -1
  389. package/dist/utils/constants.icons.test.js +0 -41
  390. package/dist/utils/planParser.test.d.ts +0 -1
  391. package/dist/utils/planParser.test.js +0 -183
  392. package/dist/utils/planningReferences.test.d.ts +0 -1
  393. package/dist/utils/planningReferences.test.js +0 -17
  394. package/dist/utils/supportContact.test.d.ts +0 -1
  395. package/dist/utils/supportContact.test.js +0 -19
  396. package/dist/utils/syncEventDetails.test.d.ts +0 -1
  397. package/dist/utils/syncEventDetails.test.js +0 -23
  398. package/dist/utils/syncEventPresentation.test.d.ts +0 -1
  399. package/dist/utils/syncEventPresentation.test.js +0 -64
  400. package/dist/utils/syncStatusPresentation.test.d.ts +0 -1
  401. package/dist/utils/syncStatusPresentation.test.js +0 -164
  402. package/dist/utils/taskActivity.test.d.ts +0 -1
  403. package/dist/utils/taskActivity.test.js +0 -218
  404. package/dist/utils/taskDraftDefaults.test.d.ts +0 -1
  405. package/dist/utils/taskDraftDefaults.test.js +0 -34
  406. package/dist/utils/taskDragZones.test.d.ts +0 -1
  407. package/dist/utils/taskDragZones.test.js +0 -14
  408. package/dist/utils/taskNormalization.test.d.ts +0 -1
  409. package/dist/utils/taskNormalization.test.js +0 -158
  410. package/dist/utils/taskSearch.test.d.ts +0 -1
  411. package/dist/utils/taskSearch.test.js +0 -69
  412. package/dist/utils/taskUtils.test.d.ts +0 -1
  413. package/dist/utils/taskUtils.test.js +0 -19
  414. package/dist/utils/taskforceApiClient.test.d.ts +0 -1
  415. package/dist/utils/taskforceApiClient.test.js +0 -44
  416. package/dist/utils/taxonomyFormDefaults.test.d.ts +0 -1
  417. package/dist/utils/taxonomyFormDefaults.test.js +0 -63
  418. package/dist/utils/uiNotice.test.d.ts +0 -1
  419. package/dist/utils/uiNotice.test.js +0 -18
  420. package/dist/utils/workspaceIdentity.test.d.ts +0 -1
  421. package/dist/utils/workspaceIdentity.test.js +0 -61
  422. package/dist/utils/workspaceLifecycle.test.d.ts +0 -1
  423. package/dist/utils/workspaceLifecycle.test.js +0 -64
  424. package/dist/utils/workspaceSyncPresentation.test.d.ts +0 -1
  425. package/dist/utils/workspaceSyncPresentation.test.js +0 -71
  426. package/scripts/bootstrap-playwright-auth.mjs +0 -155
  427. package/scripts/check-cloud-mcp.mjs +0 -83
  428. package/scripts/check-compat-boundaries.mjs +0 -41
  429. package/scripts/check-env.ts +0 -31
  430. package/scripts/check-localization-strings.mjs +0 -49
  431. package/scripts/check-runtime-boundaries.mjs +0 -44
  432. package/scripts/debug-global.mjs +0 -34
  433. package/scripts/debug-login.mjs +0 -32
  434. package/scripts/debug-server.mjs +0 -36
  435. package/scripts/deploy-prod.sh +0 -41
  436. package/scripts/deploy-staging.sh +0 -23
  437. package/scripts/export-sqlite-to-postgres.mjs +0 -123
  438. package/scripts/generate-env-example.ts +0 -8
  439. package/scripts/mcp-stdio-adapter.mjs +0 -160
  440. package/scripts/migrate-task-data-assets.mjs +0 -408
  441. package/scripts/playwright-auth.sh +0 -19
  442. package/scripts/qa-open-lib.mjs +0 -175
  443. package/scripts/qa-open-lib.mjs.d.ts +0 -49
  444. package/scripts/qa-open.mjs +0 -352
  445. package/scripts/release-version-lib.mjs +0 -88
  446. package/scripts/release-version.mjs +0 -72
  447. package/scripts/run-billing-performance-smoke.sh +0 -24
  448. package/scripts/run-dev-proxy.sh +0 -51
  449. package/scripts/run-e2e-realtime.sh +0 -30
  450. package/scripts/run-smoke.sh +0 -35
  451. package/scripts/run-soak.sh +0 -41
  452. package/scripts/test-global.js +0 -143
  453. package/scripts/update-homebrew-formula.sh +0 -108
  454. package/scripts/with-esbuild.sh +0 -11
@@ -1,1442 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { buildWorkspaceSyncPayloadModel, buildWorkspaceSyncSignatureModel } from './workspaceSyncModel';
3
- import { aiProfileSyncResourceAdapter, taskSyncResourceAdapter } from './syncResourceAdapters';
4
- describe('workspaceSyncModel', () => {
5
- describe('buildWorkspaceSyncPayloadModel', () => {
6
- it('emits document-delete when a previously pushed document path is now missing', () => {
7
- const payload = buildWorkspaceSyncPayloadModel({
8
- tasks: [],
9
- archivedTasks: [],
10
- lastPushedTaskIds: new Set(),
11
- pendingDeletedTaskIds: new Set(),
12
- documents: [
13
- { path: 'docs/keep.md', updatedAt: '2026-02-01T00:00:00.000Z', content: 'keep' }
14
- ],
15
- lastPushedDocumentPaths: new Set(['docs/keep.md', 'docs/remove.md'])
16
- });
17
- expect(Array.from(payload.currentDocumentPaths).sort()).toEqual(['docs/keep.md']);
18
- expect(Array.from(payload.deleteDocumentPaths).sort()).toEqual(['docs/remove.md']);
19
- expect(payload.changes).toEqual(expect.arrayContaining([
20
- expect.objectContaining({ op: 'document-upsert', path: 'docs/keep.md' }),
21
- expect.objectContaining({ op: 'document-delete', path: 'docs/remove.md' })
22
- ]));
23
- });
24
- it('does not emit document-delete when a canonical path is still known but missing from the readable snapshot', () => {
25
- const payload = buildWorkspaceSyncPayloadModel({
26
- tasks: [],
27
- archivedTasks: [],
28
- lastPushedTaskIds: new Set(),
29
- pendingDeletedTaskIds: new Set(),
30
- documents: [
31
- { path: 'docs/keep.md', updatedAt: '2026-02-01T00:00:00.000Z', content: 'keep' }
32
- ],
33
- currentDocumentPaths: new Set(['docs/keep.md', 'docs/missing-content.md']),
34
- lastPushedDocumentPaths: new Set(['docs/keep.md', 'docs/missing-content.md', 'docs/remove.md'])
35
- });
36
- expect(Array.from(payload.currentDocumentPaths).sort()).toEqual(['docs/keep.md', 'docs/missing-content.md']);
37
- expect(Array.from(payload.deleteDocumentPaths).sort()).toEqual(['docs/remove.md']);
38
- expect(payload.changes).toEqual(expect.arrayContaining([
39
- expect.objectContaining({ op: 'document-upsert', path: 'docs/keep.md' }),
40
- expect.objectContaining({ op: 'document-delete', path: 'docs/remove.md' })
41
- ]));
42
- expect(payload.changes).not.toEqual(expect.arrayContaining([
43
- expect.objectContaining({ op: 'document-delete', path: 'docs/missing-content.md' })
44
- ]));
45
- });
46
- it('emits task-event-upsert changes for new activity events and tracks event watermarks', () => {
47
- const payload = buildWorkspaceSyncPayloadModel({
48
- tasks: [],
49
- archivedTasks: [],
50
- taskEvents: [
51
- {
52
- id: 'task-event-1',
53
- entityType: 'task',
54
- entityId: 'task-1',
55
- taskId: 'task-1',
56
- workspaceId: 'workspace-a',
57
- action: 'task-status-changed',
58
- actor: 'ai-profile-1',
59
- actorType: 'ai',
60
- createdAt: '2026-04-13T10:05:00.000Z',
61
- details: {
62
- changes: {
63
- status: {
64
- from: 'in-progress',
65
- to: 'done'
66
- }
67
- }
68
- }
69
- }
70
- ],
71
- lastPushedTaskIds: new Set(),
72
- pendingDeletedTaskIds: new Set(),
73
- lastPushedTaskEventWatermarks: new Map()
74
- });
75
- expect(payload.changes).toEqual(expect.arrayContaining([
76
- expect.objectContaining({
77
- op: 'task-event-upsert',
78
- event: expect.objectContaining({
79
- id: 'task-event-1',
80
- taskId: 'task-1',
81
- workspaceId: 'workspace-a',
82
- action: 'task-status-changed',
83
- createdAt: '2026-04-13T10:05:00.000Z'
84
- })
85
- })
86
- ]));
87
- expect(payload.currentTaskEventWatermarks.get('task-event-1')).toBe('2026-04-13T10:05:00.000Z');
88
- });
89
- it('does not resend unchanged task events when prior task-event watermarks exist', () => {
90
- const payload = buildWorkspaceSyncPayloadModel({
91
- tasks: [],
92
- archivedTasks: [],
93
- taskEvents: [
94
- {
95
- id: 'task-event-keep',
96
- entityType: 'task',
97
- entityId: 'task-1',
98
- taskId: 'task-1',
99
- workspaceId: 'workspace-a',
100
- action: 'task-status-changed',
101
- actor: 'ai-profile-1',
102
- actorType: 'ai',
103
- createdAt: '2026-04-13T10:05:00.000Z'
104
- },
105
- {
106
- id: 'task-event-new',
107
- entityType: 'task',
108
- entityId: 'task-2',
109
- taskId: 'task-2',
110
- workspaceId: 'workspace-a',
111
- action: 'task-archived',
112
- actor: 'ai-profile-1',
113
- actorType: 'ai',
114
- createdAt: '2026-04-13T10:06:00.000Z'
115
- }
116
- ],
117
- lastPushedTaskIds: new Set(),
118
- pendingDeletedTaskIds: new Set(),
119
- lastPushedTaskEventWatermarks: new Map([
120
- ['task-event-keep', '2026-04-13T10:05:00.000Z']
121
- ])
122
- });
123
- const taskEventIds = payload.changes
124
- .filter((change) => change.op === 'task-event-upsert')
125
- .map((change) => change.event.id);
126
- expect(taskEventIds).toEqual(['task-event-new']);
127
- });
128
- it('sends only changed/new document upserts when prior document watermarks exist', () => {
129
- const payload = buildWorkspaceSyncPayloadModel({
130
- tasks: [],
131
- archivedTasks: [],
132
- lastPushedTaskIds: new Set(),
133
- pendingDeletedTaskIds: new Set(),
134
- documents: [
135
- { path: 'docs/keep.md', updatedAt: '2026-02-01T00:00:00.000Z', content: 'same' },
136
- { path: 'docs/changed.md', updatedAt: '2026-02-02T00:00:00.000Z', content: 'updated' },
137
- { path: 'docs/new.md', updatedAt: '2026-02-03T00:00:00.000Z', content: 'new' }
138
- ],
139
- lastPushedDocumentPaths: new Set(['docs/keep.md', 'docs/changed.md']),
140
- lastPushedDocumentWatermarks: new Map([
141
- ['docs/keep.md', '2026-02-01T00:00:00.000Z'],
142
- ['docs/changed.md', '2026-01-01T00:00:00.000Z']
143
- ])
144
- });
145
- const upserts = payload.changes
146
- .filter((change) => change.op === 'document-upsert')
147
- .map((change) => change.path)
148
- .sort();
149
- expect(upserts).toEqual(['docs/changed.md', 'docs/new.md']);
150
- expect(Array.from(payload.currentDocumentWatermarks.entries()).sort()).toEqual([
151
- ['docs/changed.md', '2026-02-02T00:00:00.000Z'],
152
- ['docs/keep.md', '2026-02-01T00:00:00.000Z'],
153
- ['docs/new.md', '2026-02-03T00:00:00.000Z']
154
- ]);
155
- });
156
- it('emits a document upsert when a synced document receives a newer task link', () => {
157
- const payload = buildWorkspaceSyncPayloadModel({
158
- tasks: [],
159
- archivedTasks: [],
160
- lastPushedTaskIds: new Set(),
161
- pendingDeletedTaskIds: new Set(),
162
- documents: [
163
- {
164
- path: 'workspaces/default/assets/documents/asset-doc-1-notes.md',
165
- updatedAt: '2026-03-18T14:45:00.000Z',
166
- content: '# Notes',
167
- assetId: 'asset-doc-1',
168
- documentId: 'doc-1',
169
- referenceNumber: 8,
170
- version: 1,
171
- taskId: 'task-attach-1',
172
- originalFilename: 'notes.md',
173
- linkRole: 'attachment'
174
- }
175
- ],
176
- lastPushedDocumentPaths: new Set(['workspaces/default/assets/documents/asset-doc-1-notes.md']),
177
- lastPushedDocumentWatermarks: new Map([
178
- ['workspaces/default/assets/documents/asset-doc-1-notes.md', '2026-03-18T13:30:00.000Z']
179
- ])
180
- });
181
- expect(payload.changes).toEqual(expect.arrayContaining([
182
- expect.objectContaining({
183
- op: 'document-upsert',
184
- path: 'workspaces/default/assets/documents/asset-doc-1-notes.md',
185
- updatedAt: '2026-03-18T14:45:00.000Z',
186
- taskId: 'task-attach-1',
187
- linkRole: 'attachment'
188
- })
189
- ]));
190
- });
191
- it('reports send-all diagnostics when a domain has no baseline', () => {
192
- const payload = buildWorkspaceSyncPayloadModel({
193
- tasks: [],
194
- archivedTasks: [],
195
- lastPushedTaskIds: new Set(),
196
- pendingDeletedTaskIds: new Set(),
197
- documents: [
198
- { path: 'docs/a.md', updatedAt: '2026-02-01T00:00:00.000Z', content: 'a' },
199
- { path: 'docs/b.md', updatedAt: '2026-02-02T00:00:00.000Z', content: 'b' }
200
- ],
201
- baselineResetReason: 'repair-reset'
202
- });
203
- expect(payload.deltaDiagnostics).toEqual(expect.objectContaining({
204
- totalChanges: 2,
205
- domains: expect.objectContaining({
206
- documents: expect.objectContaining({
207
- mode: 'send-all',
208
- reason: 'repair-reset',
209
- currentCount: 2,
210
- baselineCount: 0,
211
- changeCount: 2
212
- })
213
- })
214
- }));
215
- expect(payload.deltaDiagnostics?.byOp['document-upsert']).toBe(2);
216
- });
217
- it('preserves canonical document metadata on document-upsert changes', () => {
218
- const payload = buildWorkspaceSyncPayloadModel({
219
- tasks: [],
220
- archivedTasks: [],
221
- lastPushedTaskIds: new Set(),
222
- pendingDeletedTaskIds: new Set(),
223
- documents: [
224
- {
225
- path: 'workspaces/default/assets/documents/asset-doc-1-notes.md',
226
- updatedAt: '2026-03-17T12:00:00.000Z',
227
- content: '# Notes',
228
- assetId: 'asset-doc-1',
229
- documentId: 'doc-1',
230
- referenceNumber: 8,
231
- version: 7,
232
- taskId: 'task-1',
233
- originalFilename: 'notes.md',
234
- linkRole: 'attachment'
235
- }
236
- ]
237
- });
238
- expect(payload.changes).toEqual(expect.arrayContaining([
239
- expect.objectContaining({
240
- op: 'document-upsert',
241
- path: 'workspaces/default/assets/documents/asset-doc-1-notes.md',
242
- assetId: 'asset-doc-1',
243
- documentId: 'doc-1',
244
- referenceNumber: 8,
245
- version: 7,
246
- taskId: 'task-1',
247
- originalFilename: 'notes.md',
248
- linkRole: 'attachment'
249
- })
250
- ]));
251
- });
252
- it('emits asset-upsert and asset-delete changes for binary assets', () => {
253
- const payload = buildWorkspaceSyncPayloadModel({
254
- tasks: [],
255
- archivedTasks: [],
256
- lastPushedTaskIds: new Set(),
257
- pendingDeletedTaskIds: new Set(),
258
- assets: [
259
- {
260
- path: 'workspaces/default/assets/images/asset-image-1-diagram.png',
261
- updatedAt: '2026-03-17T12:00:00.000Z',
262
- contentBase64: 'aW1hZ2UtYnl0ZXM=',
263
- assetId: 'asset-image-1',
264
- kind: 'image',
265
- mimeType: 'image/png',
266
- taskId: 'task-1',
267
- originalFilename: 'diagram.png',
268
- linkRole: 'image'
269
- }
270
- ],
271
- lastPushedAssetPaths: new Set(['workspaces/default/assets/images/asset-image-1-diagram.png', 'workspaces/default/assets/files/asset-file-old-document.pdf'])
272
- });
273
- expect(Array.from(payload.currentAssetPaths)).toEqual(['workspaces/default/assets/images/asset-image-1-diagram.png']);
274
- expect(Array.from(payload.deleteAssetPaths)).toEqual(['workspaces/default/assets/files/asset-file-old-document.pdf']);
275
- expect(payload.changes).toEqual(expect.arrayContaining([
276
- expect.objectContaining({
277
- op: 'asset-upsert',
278
- path: 'workspaces/default/assets/images/asset-image-1-diagram.png',
279
- assetId: 'asset-image-1',
280
- kind: 'image',
281
- linkRole: 'image'
282
- }),
283
- expect.objectContaining({
284
- op: 'asset-delete',
285
- path: 'workspaces/default/assets/files/asset-file-old-document.pdf'
286
- })
287
- ]));
288
- });
289
- it('does not emit asset-delete when a canonical asset path is still known but missing from the readable snapshot', () => {
290
- const payload = buildWorkspaceSyncPayloadModel({
291
- tasks: [],
292
- archivedTasks: [],
293
- lastPushedTaskIds: new Set(),
294
- pendingDeletedTaskIds: new Set(),
295
- assets: [
296
- {
297
- path: 'workspaces/default/assets/images/asset-image-1-diagram.png',
298
- updatedAt: '2026-03-17T12:00:00.000Z',
299
- contentBase64: 'aW1hZ2UtYnl0ZXM=',
300
- assetId: 'asset-image-1',
301
- kind: 'image',
302
- mimeType: 'image/png',
303
- taskId: 'task-1',
304
- originalFilename: 'diagram.png',
305
- linkRole: 'image'
306
- }
307
- ],
308
- currentAssetPaths: new Set([
309
- 'workspaces/default/assets/images/asset-image-1-diagram.png',
310
- 'workspaces/default/assets/files/asset-file-missing.pdf'
311
- ]),
312
- lastPushedAssetPaths: new Set([
313
- 'workspaces/default/assets/images/asset-image-1-diagram.png',
314
- 'workspaces/default/assets/files/asset-file-missing.pdf',
315
- 'workspaces/default/assets/files/asset-file-old-document.pdf'
316
- ])
317
- });
318
- expect(Array.from(payload.currentAssetPaths).sort()).toEqual([
319
- 'workspaces/default/assets/files/asset-file-missing.pdf',
320
- 'workspaces/default/assets/images/asset-image-1-diagram.png'
321
- ]);
322
- expect(Array.from(payload.deleteAssetPaths)).toEqual(['workspaces/default/assets/files/asset-file-old-document.pdf']);
323
- expect(payload.changes).not.toEqual(expect.arrayContaining([
324
- expect.objectContaining({
325
- op: 'asset-delete',
326
- path: 'workspaces/default/assets/files/asset-file-missing.pdf'
327
- })
328
- ]));
329
- });
330
- it('emits timestamped task deletes for previously pushed tasks that are now missing', () => {
331
- const payload = buildWorkspaceSyncPayloadModel({
332
- tasks: [],
333
- archivedTasks: [],
334
- lastPushedTaskIds: new Set(['task-delete-1']),
335
- pendingDeletedTaskIds: new Set()
336
- });
337
- expect(payload.deleteTaskIds).toEqual(new Set(['task-delete-1']));
338
- expect(payload.changes).toEqual(expect.arrayContaining([
339
- expect.objectContaining({
340
- op: 'delete',
341
- taskId: 'task-delete-1',
342
- deletedAt: expect.any(String)
343
- })
344
- ]));
345
- });
346
- it('preserves stored task delete watermarks on pending delete changes', () => {
347
- const payload = buildWorkspaceSyncPayloadModel({
348
- tasks: [],
349
- archivedTasks: [],
350
- lastPushedTaskIds: new Set(),
351
- pendingDeletedTaskIds: new Set(['task-delete-2']),
352
- pendingDeletedTaskWatermarks: new Map([
353
- ['task-delete-2', '2026-04-10T11:00:00.000Z']
354
- ])
355
- });
356
- expect(payload.changes).toEqual(expect.arrayContaining([
357
- expect.objectContaining({
358
- op: 'delete',
359
- taskId: 'task-delete-2',
360
- deletedAt: '2026-04-10T11:00:00.000Z'
361
- })
362
- ]));
363
- });
364
- it('orders task upserts deterministically by task id', () => {
365
- const payload = buildWorkspaceSyncPayloadModel({
366
- tasks: [
367
- {
368
- id: 'parent-1',
369
- title: 'Parent',
370
- status: 'done',
371
- createdAt: '2026-03-15T12:00:00.000Z',
372
- updatedAt: '2026-03-15T12:05:00.000Z'
373
- },
374
- {
375
- id: 'child-1',
376
- title: 'Child',
377
- status: 'done',
378
- createdAt: '2026-03-15T12:00:00.000Z',
379
- updatedAt: '2026-03-15T12:04:00.000Z'
380
- }
381
- ],
382
- archivedTasks: [],
383
- lastPushedTaskIds: new Set(),
384
- pendingDeletedTaskIds: new Set()
385
- });
386
- const upserts = payload.changes
387
- .filter((change) => change.op === 'upsert')
388
- .map((change) => (change.op === 'upsert' ? String(change.task.id) : ''));
389
- expect(upserts).toEqual(['child-1', 'parent-1']);
390
- });
391
- it('emits ai-profile-upsert changes and tracks ai profile watermarks', () => {
392
- const payload = buildWorkspaceSyncPayloadModel({
393
- tasks: [],
394
- archivedTasks: [],
395
- lastPushedTaskIds: new Set(),
396
- pendingDeletedTaskIds: new Set(),
397
- aiProfiles: [
398
- {
399
- id: 'ai-profile-1',
400
- workspaceId: 'ws-1',
401
- profileToken: 'tfp_test',
402
- name: 'Codex',
403
- username: 'codex',
404
- icon: 'Bot',
405
- color: '#0ea5e9',
406
- description: 'Terminal-first coding agent',
407
- role: 'Implementer',
408
- provider: 'OpenAI',
409
- model: 'gpt-5.4',
410
- surfaceType: 'coding_tool',
411
- rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z',
412
- createdAt: '2026-03-22T10:00:00.000Z',
413
- updatedAt: '2026-03-22T10:05:00.000Z',
414
- lastActiveAt: '2026-03-22T10:05:00.000Z'
415
- }
416
- ]
417
- });
418
- expect(payload.changes).toEqual(expect.arrayContaining([
419
- expect.objectContaining({
420
- op: 'ai-profile-upsert',
421
- profile: expect.objectContaining({
422
- id: 'ai-profile-1',
423
- name: 'Codex',
424
- description: 'Terminal-first coding agent',
425
- role: 'Implementer',
426
- provider: 'OpenAI',
427
- model: 'gpt-5.4',
428
- surfaceType: 'coding_tool',
429
- avatarRevision: 0,
430
- avatarUpdatedAt: null,
431
- rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z'
432
- })
433
- })
434
- ]));
435
- expect(Array.from(payload.currentAiProfileIds)).toEqual(['ai-profile-1']);
436
- expect(payload.currentAiProfileWatermarks.get('ai-profile-1')).toBe('2026-03-22T10:05:00.000Z');
437
- });
438
- it('filters unchanged ai profiles when ai change-key state already matches', () => {
439
- const profile = {
440
- id: 'ai-profile-1',
441
- workspaceId: 'ws-1',
442
- profileToken: 'tfp_test',
443
- name: 'Codex',
444
- username: 'codex',
445
- icon: 'Bot',
446
- color: '#0ea5e9',
447
- description: 'Terminal-first coding agent',
448
- role: 'Implementer',
449
- provider: 'OpenAI',
450
- model: 'gpt-5.4',
451
- surfaceType: 'coding_tool',
452
- rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z',
453
- createdAt: '2026-03-22T10:00:00.000Z',
454
- updatedAt: '2026-03-22T10:05:00.000Z',
455
- lastActiveAt: '2026-03-22T10:05:00.000Z'
456
- };
457
- const payload = buildWorkspaceSyncPayloadModel({
458
- tasks: [],
459
- archivedTasks: [],
460
- lastPushedTaskIds: new Set(),
461
- pendingDeletedTaskIds: new Set(),
462
- aiProfiles: [profile],
463
- lastPushedAiProfileIds: new Set(['ai-profile-1']),
464
- lastPushedAiProfileWatermarks: new Map([['ai-profile-1', '2026-03-22T10:05:00.000Z']]),
465
- lastPushedAiProfileChangeKeys: new Map([
466
- ['ai-profile-1', aiProfileSyncResourceAdapter.getChangeKey(profile)]
467
- ])
468
- });
469
- expect(payload.changes.some((change) => change.op === 'ai-profile-upsert')).toBe(false);
470
- expect(Array.from(payload.currentAiProfileIds)).toEqual(['ai-profile-1']);
471
- });
472
- it('resends ai profiles when a legacy watermark did not include avatar fields', () => {
473
- const payload = buildWorkspaceSyncPayloadModel({
474
- tasks: [],
475
- archivedTasks: [],
476
- lastPushedTaskIds: new Set(),
477
- pendingDeletedTaskIds: new Set(),
478
- aiProfiles: [
479
- {
480
- id: 'ai-profile-1',
481
- workspaceId: 'ws-1',
482
- profileToken: 'tfp_test',
483
- name: 'Codex',
484
- username: 'codex',
485
- icon: 'Bot',
486
- color: '#0ea5e9',
487
- avatarUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Favatar.webp',
488
- avatarSourceUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Fsource.webp',
489
- avatarRevision: 1,
490
- avatarUpdatedAt: '2026-03-22T10:04:00.000Z',
491
- description: 'Terminal-first coding agent',
492
- role: 'Implementer',
493
- provider: 'OpenAI',
494
- model: 'gpt-5.4',
495
- surfaceType: 'coding_tool',
496
- rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z',
497
- createdAt: '2026-03-22T10:00:00.000Z',
498
- updatedAt: '2026-03-22T10:05:00.000Z',
499
- lastActiveAt: '2026-03-22T10:05:00.000Z'
500
- }
501
- ],
502
- lastPushedAiProfileIds: new Set(['ai-profile-1']),
503
- lastPushedAiProfileWatermarks: new Map([['ai-profile-1', '2026-03-22T10:05:00.000Z']])
504
- });
505
- expect(payload.changes).toEqual(expect.arrayContaining([
506
- expect.objectContaining({
507
- op: 'ai-profile-upsert',
508
- profile: expect.objectContaining({
509
- id: 'ai-profile-1',
510
- avatarUrl: expect.stringContaining('/api/taskforce/context/'),
511
- avatarSourceUrl: expect.stringContaining('/api/taskforce/context/'),
512
- avatarRevision: 1,
513
- avatarUpdatedAt: '2026-03-22T10:04:00.000Z'
514
- })
515
- })
516
- ]));
517
- expect(payload.currentAiProfileWatermarks.get('ai-profile-1')).toBe('2026-03-22T10:05:00.000Z');
518
- expect(payload.currentAiProfileChangeKeys?.get('ai-profile-1')).toBe(aiProfileSyncResourceAdapter.getChangeKey({
519
- id: 'ai-profile-1',
520
- workspaceId: 'ws-1',
521
- profileToken: 'tfp_test',
522
- name: 'Codex',
523
- username: 'codex',
524
- icon: 'Bot',
525
- color: '#0ea5e9',
526
- avatarUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Favatar.webp',
527
- avatarSourceUrl: '/api/taskforce/context/workspaces%2Fws-1%2Fassets%2Fimages%2Fai-profiles%2Fai-profile-1%2Favatar%2Fsource.webp',
528
- avatarRevision: 1,
529
- avatarUpdatedAt: '2026-03-22T10:04:00.000Z',
530
- description: 'Terminal-first coding agent',
531
- role: 'Implementer',
532
- provider: 'OpenAI',
533
- model: 'gpt-5.4',
534
- surfaceType: 'coding_tool',
535
- rosterStateUpdatedAt: '2026-03-22T10:00:00.000Z',
536
- createdAt: '2026-03-22T10:00:00.000Z',
537
- updatedAt: '2026-03-22T10:05:00.000Z',
538
- lastActiveAt: '2026-03-22T10:05:00.000Z'
539
- }));
540
- });
541
- it('resends ai profiles when lifecycle state advances beyond metadata updatedAt', () => {
542
- const payload = buildWorkspaceSyncPayloadModel({
543
- tasks: [],
544
- archivedTasks: [],
545
- lastPushedTaskIds: new Set(),
546
- pendingDeletedTaskIds: new Set(),
547
- aiProfiles: [
548
- {
549
- id: 'ai-profile-1',
550
- workspaceId: 'ws-1',
551
- profileToken: 'tfp_test',
552
- name: 'Codex',
553
- username: 'codex',
554
- icon: 'Bot',
555
- color: '#0ea5e9',
556
- description: 'Terminal-first coding agent',
557
- role: 'Implementer',
558
- provider: 'OpenAI',
559
- model: 'gpt-5.4',
560
- surfaceType: 'coding_tool',
561
- archivedAt: '2026-03-22T10:10:00.000Z',
562
- archivedBy: 'owner-1',
563
- archivedReason: 'manual_archive',
564
- rosterStateUpdatedAt: '2026-03-22T10:10:00.000Z',
565
- createdAt: '2026-03-22T10:00:00.000Z',
566
- updatedAt: '2026-03-22T10:05:00.000Z',
567
- lastActiveAt: '2026-03-22T10:05:00.000Z'
568
- }
569
- ],
570
- lastPushedAiProfileIds: new Set(['ai-profile-1']),
571
- lastPushedAiProfileWatermarks: new Map([['ai-profile-1', '2026-03-22T10:05:00.000Z']])
572
- });
573
- expect(payload.changes).toEqual(expect.arrayContaining([
574
- expect.objectContaining({
575
- op: 'ai-profile-upsert',
576
- profile: expect.objectContaining({
577
- id: 'ai-profile-1',
578
- archivedAt: '2026-03-22T10:10:00.000Z',
579
- rosterStateUpdatedAt: '2026-03-22T10:10:00.000Z',
580
- updatedAt: '2026-03-22T10:05:00.000Z'
581
- })
582
- })
583
- ]));
584
- expect(payload.currentAiProfileWatermarks.get('ai-profile-1')).toBe('2026-03-22T10:10:00.000Z');
585
- });
586
- it('emits initiative and workstream upserts and tracks their watermarks', () => {
587
- const payload = buildWorkspaceSyncPayloadModel({
588
- tasks: [],
589
- archivedTasks: [],
590
- lastPushedTaskIds: new Set(),
591
- pendingDeletedTaskIds: new Set(),
592
- initiatives: [{
593
- id: 'initiative-1',
594
- referenceNumber: 12,
595
- referenceLabel: 'IN-12',
596
- title: 'Revenue Expansion',
597
- description: 'Grow subscription revenue',
598
- ownerId: 'user-1',
599
- createdAt: '2026-03-29T10:00:00.000Z',
600
- updatedAt: '2026-03-29T11:00:00.000Z',
601
- order: 1,
602
- attachments: [{
603
- path: 'workspaces/ws-1/assets/documents/asset-in.md',
604
- assetId: 'asset-in',
605
- referenceLabel: 'D-31',
606
- caption: 'Revenue context'
607
- }],
608
- isArchived: false
609
- }],
610
- workstreams: [{
611
- id: 'workstream-1',
612
- referenceNumber: 4,
613
- referenceLabel: 'WS-4',
614
- initiativeId: 'initiative-1',
615
- title: 'Checkout UX',
616
- description: 'Improve payment flow',
617
- ownerId: 'user-2',
618
- createdAt: '2026-03-29T10:30:00.000Z',
619
- updatedAt: '2026-03-29T11:30:00.000Z',
620
- order: 2,
621
- attachments: [{
622
- path: 'workspaces/ws-1/assets/documents/asset-ws.md',
623
- assetId: 'asset-ws',
624
- referenceLabel: 'D-32',
625
- caption: 'Checkout context'
626
- }],
627
- isArchived: false
628
- }]
629
- });
630
- expect(payload.changes).toEqual(expect.arrayContaining([
631
- expect.objectContaining({
632
- op: 'initiative-upsert',
633
- initiative: expect.objectContaining({
634
- id: 'initiative-1',
635
- title: 'Revenue Expansion',
636
- attachments: expect.arrayContaining([
637
- expect.objectContaining({
638
- path: 'workspaces/ws-1/assets/documents/asset-in.md',
639
- referenceLabel: 'D-31'
640
- })
641
- ])
642
- })
643
- }),
644
- expect.objectContaining({
645
- op: 'workstream-upsert',
646
- workstream: expect.objectContaining({
647
- id: 'workstream-1',
648
- initiativeId: 'initiative-1',
649
- title: 'Checkout UX',
650
- attachments: expect.arrayContaining([
651
- expect.objectContaining({
652
- path: 'workspaces/ws-1/assets/documents/asset-ws.md',
653
- referenceLabel: 'D-32'
654
- })
655
- ])
656
- })
657
- })
658
- ]));
659
- expect(Array.from(payload.currentInitiativeIds)).toEqual(['initiative-1']);
660
- expect(Array.from(payload.currentWorkstreamIds)).toEqual(['workstream-1']);
661
- expect(payload.currentInitiativeWatermarks.get('initiative-1')).toBe('2026-03-29T11:00:00.000Z');
662
- expect(payload.currentWorkstreamWatermarks.get('workstream-1')).toBe('2026-03-29T11:30:00.000Z');
663
- });
664
- it('filters unchanged initiative and workstream upserts when watermarks already match', () => {
665
- const payload = buildWorkspaceSyncPayloadModel({
666
- tasks: [],
667
- archivedTasks: [],
668
- lastPushedTaskIds: new Set(),
669
- pendingDeletedTaskIds: new Set(),
670
- initiatives: [{
671
- id: 'initiative-1',
672
- title: 'Revenue Expansion',
673
- description: null,
674
- createdAt: '2026-03-29T10:00:00.000Z',
675
- updatedAt: '2026-03-29T11:00:00.000Z'
676
- }],
677
- workstreams: [{
678
- id: 'workstream-1',
679
- initiativeId: 'initiative-1',
680
- title: 'Checkout UX',
681
- description: null,
682
- createdAt: '2026-03-29T10:30:00.000Z',
683
- updatedAt: '2026-03-29T11:30:00.000Z'
684
- }],
685
- lastPushedInitiativeWatermarks: new Map([['initiative-1', '2026-03-29T11:00:00.000Z']]),
686
- lastPushedWorkstreamWatermarks: new Map([['workstream-1', '2026-03-29T11:30:00.000Z']])
687
- });
688
- expect(payload.changes.some((change) => change.op === 'initiative-upsert')).toBe(false);
689
- expect(payload.changes.some((change) => change.op === 'workstream-upsert')).toBe(false);
690
- expect(Array.from(payload.currentInitiativeIds)).toEqual(['initiative-1']);
691
- expect(Array.from(payload.currentWorkstreamIds)).toEqual(['workstream-1']);
692
- });
693
- it('includes unattached annotated attachment sessions in sync payloads', () => {
694
- const payload = buildWorkspaceSyncPayloadModel({
695
- tasks: [],
696
- archivedTasks: [],
697
- lastPushedTaskIds: new Set(),
698
- pendingDeletedTaskIds: new Set(),
699
- annotatedAttachmentSessions: [
700
- {
701
- id: 'session-unattached-1',
702
- workspaceId: 'ws-1',
703
- taskId: '',
704
- baseImageAssetId: 'asset-image-1',
705
- title: 'Loose image notes',
706
- globalInstruction: null,
707
- annotations: [],
708
- createdByActorId: null,
709
- updatedByActorId: null,
710
- createdAt: '2026-03-30T00:00:00.000Z',
711
- updatedAt: '2026-03-30T00:05:00.000Z',
712
- deletedAt: null
713
- }
714
- ]
715
- });
716
- expect(payload.changes).toEqual(expect.arrayContaining([
717
- expect.objectContaining({
718
- op: 'annotated-attachment-session-upsert',
719
- session: expect.objectContaining({
720
- id: 'session-unattached-1',
721
- taskId: '',
722
- baseImageAssetId: 'asset-image-1'
723
- })
724
- })
725
- ]));
726
- expect(Array.from(payload.currentAnnotatedAttachmentSessionIds)).toEqual(['session-unattached-1']);
727
- expect(payload.currentAnnotatedAttachmentSessionWatermarks.get('session-unattached-1')).toBe('2026-03-30T00:05:00.000Z');
728
- });
729
- it('includes all ai profiles when ai watermark state is intentionally reset', () => {
730
- const payload = buildWorkspaceSyncPayloadModel({
731
- tasks: [],
732
- archivedTasks: [],
733
- lastPushedTaskIds: new Set(),
734
- pendingDeletedTaskIds: new Set(),
735
- aiProfiles: [
736
- {
737
- id: 'ai-profile-1',
738
- workspaceId: 'ws-1',
739
- profileToken: 'tfp_test',
740
- name: 'Codex',
741
- username: 'codex',
742
- icon: 'Bot',
743
- color: '#0ea5e9',
744
- createdAt: '2026-03-22T10:00:00.000Z',
745
- updatedAt: '2026-03-22T10:05:00.000Z',
746
- lastActiveAt: '2026-03-22T10:05:00.000Z'
747
- }
748
- ],
749
- lastPushedAiProfileIds: new Set(),
750
- lastPushedAiProfileWatermarks: new Map()
751
- });
752
- expect(payload.changes).toEqual(expect.arrayContaining([
753
- expect.objectContaining({
754
- op: 'ai-profile-upsert',
755
- profile: expect.objectContaining({
756
- id: 'ai-profile-1'
757
- })
758
- })
759
- ]));
760
- });
761
- it('emits document review session upserts and deletes with watermark tracking', () => {
762
- const payload = buildWorkspaceSyncPayloadModel({
763
- tasks: [],
764
- archivedTasks: [],
765
- lastPushedTaskIds: new Set(),
766
- pendingDeletedTaskIds: new Set(),
767
- documentReviewSessions: [
768
- {
769
- id: 'review-keep',
770
- assetId: 'asset-doc-1',
771
- documentId: 'doc-1',
772
- documentVersion: 2,
773
- title: 'Review',
774
- status: 'open',
775
- comments: [],
776
- createdByActorId: null,
777
- updatedByActorId: null,
778
- createdAt: '2026-03-24T10:00:00.000Z',
779
- updatedAt: '2026-03-24T10:05:00.000Z',
780
- deletedAt: null
781
- }
782
- ],
783
- lastPushedDocumentReviewSessionIds: new Set(['review-keep', 'review-delete']),
784
- lastPushedDocumentReviewSessionWatermarks: new Map([
785
- ['review-delete', '2026-03-23T10:00:00.000Z']
786
- ])
787
- });
788
- expect(Array.from(payload.currentDocumentReviewSessionIds)).toEqual(['review-keep']);
789
- expect(Array.from(payload.deleteDocumentReviewSessionIds)).toEqual(['review-delete']);
790
- expect(payload.currentDocumentReviewSessionWatermarks.get('review-keep')).toBe('2026-03-24T10:05:00.000Z');
791
- expect(payload.changes).toEqual(expect.arrayContaining([
792
- expect.objectContaining({
793
- op: 'document-review-session-upsert',
794
- session: expect.objectContaining({ id: 'review-keep', assetId: 'asset-doc-1' })
795
- }),
796
- expect.objectContaining({
797
- op: 'document-review-session-delete',
798
- sessionId: 'review-delete'
799
- })
800
- ]));
801
- });
802
- it('emits document review comment upserts and deletes with watermark tracking', () => {
803
- const payload = buildWorkspaceSyncPayloadModel({
804
- tasks: [],
805
- archivedTasks: [],
806
- lastPushedTaskIds: new Set(),
807
- pendingDeletedTaskIds: new Set(),
808
- documentReviewComments: [
809
- {
810
- id: 'comment-keep',
811
- sessionId: 'review-1',
812
- body: 'Keep this note.',
813
- anchor: { kind: 'document' },
814
- order: 0,
815
- authorActorId: null,
816
- createdAt: '2026-03-24T10:00:00.000Z',
817
- updatedAt: '2026-03-24T10:05:00.000Z',
818
- deletedAt: null
819
- }
820
- ],
821
- lastPushedDocumentReviewCommentIds: new Set(['comment-keep', 'comment-delete']),
822
- lastPushedDocumentReviewCommentWatermarks: new Map([
823
- ['comment-delete', '2026-03-23T10:00:00.000Z']
824
- ])
825
- });
826
- expect(Array.from(payload.currentDocumentReviewCommentIds)).toEqual(['comment-keep']);
827
- expect(Array.from(payload.deleteDocumentReviewCommentIds)).toEqual(['comment-delete']);
828
- expect(payload.currentDocumentReviewCommentWatermarks.get('comment-keep')).toBe('2026-03-24T10:05:00.000Z');
829
- expect(payload.changes).toEqual(expect.arrayContaining([
830
- expect.objectContaining({
831
- op: 'document-review-comment-upsert',
832
- comment: expect.objectContaining({ id: 'comment-keep', sessionId: 'review-1' })
833
- }),
834
- expect.objectContaining({
835
- op: 'document-review-comment-delete',
836
- commentId: 'comment-delete'
837
- })
838
- ]));
839
- });
840
- it('does not resend already-acknowledged deleted collaboration records', () => {
841
- const payload = buildWorkspaceSyncPayloadModel({
842
- tasks: [],
843
- archivedTasks: [],
844
- lastPushedTaskIds: new Set(),
845
- pendingDeletedTaskIds: new Set(),
846
- documentReviewSessions: [
847
- {
848
- id: 'review-deleted',
849
- assetId: 'asset-doc-1',
850
- documentId: 'doc-1',
851
- documentVersion: 2,
852
- title: 'Review',
853
- status: 'open',
854
- comments: [],
855
- createdByActorId: null,
856
- updatedByActorId: null,
857
- createdAt: '2026-03-24T10:00:00.000Z',
858
- updatedAt: '2026-03-24T10:05:00.000Z',
859
- deletedAt: '2026-03-24T10:06:00.000Z'
860
- }
861
- ],
862
- documentReviewComments: [
863
- {
864
- id: 'comment-deleted',
865
- sessionId: 'review-deleted',
866
- body: 'Delete me.',
867
- anchor: { kind: 'document' },
868
- order: 0,
869
- authorActorId: null,
870
- createdAt: '2026-03-24T10:00:00.000Z',
871
- updatedAt: '2026-03-24T10:05:00.000Z',
872
- deletedAt: '2026-03-24T10:06:30.000Z'
873
- }
874
- ],
875
- annotatedAttachmentSessions: [
876
- {
877
- id: 'annotation-deleted',
878
- workspaceId: 'ws-1',
879
- taskId: 'task-1',
880
- baseImageAssetId: 'asset-image-1',
881
- title: 'Annotations',
882
- globalInstruction: null,
883
- annotations: [],
884
- createdByActorId: null,
885
- updatedByActorId: null,
886
- createdAt: '2026-03-24T10:00:00.000Z',
887
- updatedAt: '2026-03-24T10:05:00.000Z',
888
- deletedAt: '2026-03-24T10:07:00.000Z'
889
- }
890
- ],
891
- lastPushedDocumentReviewSessionIds: new Set(),
892
- lastPushedDocumentReviewSessionWatermarks: new Map([
893
- ['review-deleted', '2026-03-24T10:06:00.000Z']
894
- ]),
895
- lastPushedDocumentReviewCommentIds: new Set(),
896
- lastPushedDocumentReviewCommentWatermarks: new Map([
897
- ['comment-deleted', '2026-03-24T10:06:30.000Z']
898
- ]),
899
- lastPushedAnnotatedAttachmentSessionIds: new Set(),
900
- lastPushedAnnotatedAttachmentSessionWatermarks: new Map([
901
- ['annotation-deleted', '2026-03-24T10:07:00.000Z']
902
- ])
903
- });
904
- expect(payload.currentDocumentReviewSessionWatermarks.get('review-deleted')).toBe('2026-03-24T10:06:00.000Z');
905
- expect(payload.currentDocumentReviewCommentWatermarks.get('comment-deleted')).toBe('2026-03-24T10:06:30.000Z');
906
- expect(payload.currentAnnotatedAttachmentSessionWatermarks.get('annotation-deleted')).toBe('2026-03-24T10:07:00.000Z');
907
- expect(payload.changes).not.toEqual(expect.arrayContaining([
908
- expect.objectContaining({ op: 'document-review-session-delete', sessionId: 'review-deleted' }),
909
- expect.objectContaining({ op: 'document-review-comment-delete', commentId: 'comment-deleted' }),
910
- expect.objectContaining({ op: 'annotated-attachment-session-delete', sessionId: 'annotation-deleted' })
911
- ]));
912
- expect(payload.deltaDiagnostics?.domains.documentReviewSessions.changeCount).toBe(0);
913
- expect(payload.deltaDiagnostics?.domains.documentReviewComments.changeCount).toBe(0);
914
- expect(payload.deltaDiagnostics?.domains.annotatedAttachmentSessions.changeCount).toBe(0);
915
- });
916
- it('emits a single delete op when a previously pushed collaboration record is now soft-deleted', () => {
917
- const payload = buildWorkspaceSyncPayloadModel({
918
- tasks: [],
919
- archivedTasks: [],
920
- lastPushedTaskIds: new Set(),
921
- pendingDeletedTaskIds: new Set(),
922
- documentReviewSessions: [{
923
- id: 'review-deleted',
924
- assetId: 'asset-doc-1',
925
- documentId: 'doc-1',
926
- documentVersion: 2,
927
- title: 'Review',
928
- status: 'open',
929
- comments: [],
930
- createdByActorId: null,
931
- updatedByActorId: null,
932
- createdAt: '2026-03-24T10:00:00.000Z',
933
- updatedAt: '2026-03-24T10:05:00.000Z',
934
- deletedAt: '2026-03-24T10:06:00.000Z'
935
- }],
936
- documentReviewComments: [{
937
- id: 'comment-deleted',
938
- sessionId: 'review-deleted',
939
- body: 'Delete me.',
940
- anchor: { kind: 'document' },
941
- order: 0,
942
- authorActorId: null,
943
- createdAt: '2026-03-24T10:00:00.000Z',
944
- updatedAt: '2026-03-24T10:05:00.000Z',
945
- deletedAt: '2026-03-24T10:06:30.000Z'
946
- }],
947
- annotatedAttachmentSessions: [{
948
- id: 'annotation-deleted',
949
- workspaceId: 'ws-1',
950
- taskId: 'task-1',
951
- baseImageAssetId: 'asset-image-1',
952
- title: 'Annotations',
953
- globalInstruction: null,
954
- annotations: [],
955
- createdByActorId: null,
956
- updatedByActorId: null,
957
- createdAt: '2026-03-24T10:00:00.000Z',
958
- updatedAt: '2026-03-24T10:05:00.000Z',
959
- deletedAt: '2026-03-24T10:07:00.000Z'
960
- }],
961
- lastPushedDocumentReviewSessionIds: new Set(['review-deleted']),
962
- lastPushedDocumentReviewSessionWatermarks: new Map(),
963
- lastPushedDocumentReviewCommentIds: new Set(['comment-deleted']),
964
- lastPushedDocumentReviewCommentWatermarks: new Map(),
965
- lastPushedAnnotatedAttachmentSessionIds: new Set(['annotation-deleted']),
966
- lastPushedAnnotatedAttachmentSessionWatermarks: new Map()
967
- });
968
- expect(payload.changes.filter((change) => change.op === 'document-review-session-delete')).toHaveLength(1);
969
- expect(payload.changes.filter((change) => change.op === 'document-review-comment-delete')).toHaveLength(1);
970
- expect(payload.changes.filter((change) => change.op === 'annotated-attachment-session-delete')).toHaveLength(1);
971
- });
972
- });
973
- describe('buildWorkspaceSyncSignatureModel', () => {
974
- it('includes pending delete operations in the signature hash', () => {
975
- const noDeletes = buildWorkspaceSyncSignatureModel({
976
- workspaceId: 'ws-1',
977
- tasks: [{ id: 'task-1', status: 'task', title: 'Task 1' }],
978
- archivedTasks: [],
979
- pendingDeletedTaskIds: new Set()
980
- });
981
- const withDeletes = buildWorkspaceSyncSignatureModel({
982
- workspaceId: 'ws-1',
983
- tasks: [{ id: 'task-1', status: 'task', title: 'Task 1' }],
984
- archivedTasks: [],
985
- pendingDeletedTaskIds: new Set(['task-2'])
986
- });
987
- const parsedNoDeletes = JSON.parse(noDeletes);
988
- const parsedWithDeletes = JSON.parse(withDeletes);
989
- expect(parsedNoDeletes.pendingDeletes).toEqual([]);
990
- expect(parsedWithDeletes.pendingDeletes).toEqual(['task-2']);
991
- expect(noDeletes).not.toEqual(withDeletes);
992
- });
993
- it('changes when status changes even if task membership does not', () => {
994
- const baseTask = {
995
- id: 'task-1',
996
- title: 'Task 1',
997
- status: 'task',
998
- updatedAt: '2026-03-16T10:00:00.000Z'
999
- };
1000
- const baseSignature = buildWorkspaceSyncSignatureModel({
1001
- workspaceId: 'ws-1',
1002
- tasks: [baseTask],
1003
- archivedTasks: [],
1004
- pendingDeletedTaskIds: new Set()
1005
- });
1006
- const inProgressSignature = buildWorkspaceSyncSignatureModel({
1007
- workspaceId: 'ws-1',
1008
- tasks: [{ ...baseTask, status: 'in-progress' }],
1009
- archivedTasks: [],
1010
- pendingDeletedTaskIds: new Set()
1011
- });
1012
- expect(inProgressSignature).not.toEqual(baseSignature);
1013
- });
1014
- it('changes when synced ai profile identity changes', () => {
1015
- const baseSignature = buildWorkspaceSyncSignatureModel({
1016
- workspaceId: 'ws-1',
1017
- tasks: [],
1018
- archivedTasks: [],
1019
- aiProfiles: [{
1020
- id: 'ai-profile-1',
1021
- workspaceId: 'ws-1',
1022
- profileToken: 'tfp_test',
1023
- name: 'Codex',
1024
- username: 'codex',
1025
- icon: 'Bot',
1026
- color: '#0ea5e9',
1027
- createdAt: '2026-03-22T10:00:00.000Z',
1028
- updatedAt: '2026-03-22T10:05:00.000Z'
1029
- }]
1030
- });
1031
- const renamedSignature = buildWorkspaceSyncSignatureModel({
1032
- workspaceId: 'ws-1',
1033
- tasks: [],
1034
- archivedTasks: [],
1035
- aiProfiles: [{
1036
- id: 'ai-profile-1',
1037
- workspaceId: 'ws-1',
1038
- profileToken: 'tfp_test',
1039
- name: 'Codex 2',
1040
- username: 'codex',
1041
- icon: 'Bot',
1042
- color: '#0ea5e9',
1043
- createdAt: '2026-03-22T10:00:00.000Z',
1044
- updatedAt: '2026-03-22T10:06:00.000Z'
1045
- }]
1046
- });
1047
- expect(renamedSignature).not.toEqual(baseSignature);
1048
- });
1049
- it('keeps ai profile fingerprint stable when surfaceType stays null', () => {
1050
- const baseSignature = buildWorkspaceSyncSignatureModel({
1051
- workspaceId: 'ws-1',
1052
- tasks: [],
1053
- archivedTasks: [],
1054
- aiProfiles: [{
1055
- id: 'ai-profile-1',
1056
- workspaceId: 'ws-1',
1057
- profileToken: 'tfp_test',
1058
- name: 'John Drake',
1059
- username: 'john-drake',
1060
- icon: 'Bot',
1061
- color: '#64748b',
1062
- surfaceType: null,
1063
- createdAt: '2026-03-22T10:00:00.000Z',
1064
- updatedAt: '2026-03-22T10:05:00.000Z'
1065
- }]
1066
- });
1067
- const repeatedNullSignature = buildWorkspaceSyncSignatureModel({
1068
- workspaceId: 'ws-1',
1069
- tasks: [],
1070
- archivedTasks: [],
1071
- aiProfiles: [{
1072
- id: 'ai-profile-1',
1073
- workspaceId: 'ws-1',
1074
- profileToken: 'tfp_test',
1075
- name: 'John Drake',
1076
- username: 'john-drake',
1077
- icon: 'Bot',
1078
- color: '#64748b',
1079
- surfaceType: null,
1080
- createdAt: '2026-03-22T10:00:00.000Z',
1081
- updatedAt: '2026-03-22T10:05:00.000Z'
1082
- }]
1083
- });
1084
- expect(repeatedNullSignature).toEqual(baseSignature);
1085
- });
1086
- it('changes when initiative or workstream state changes', () => {
1087
- const baseSignature = buildWorkspaceSyncSignatureModel({
1088
- workspaceId: 'ws-1',
1089
- tasks: [],
1090
- archivedTasks: [],
1091
- initiatives: [{
1092
- id: 'initiative-1',
1093
- title: 'Revenue Expansion',
1094
- description: null,
1095
- createdAt: '2026-03-29T10:00:00.000Z',
1096
- updatedAt: '2026-03-29T11:00:00.000Z'
1097
- }],
1098
- workstreams: [{
1099
- id: 'workstream-1',
1100
- initiativeId: 'initiative-1',
1101
- title: 'Checkout UX',
1102
- description: null,
1103
- createdAt: '2026-03-29T10:30:00.000Z',
1104
- updatedAt: '2026-03-29T11:30:00.000Z'
1105
- }]
1106
- });
1107
- const changedSignature = buildWorkspaceSyncSignatureModel({
1108
- workspaceId: 'ws-1',
1109
- tasks: [],
1110
- archivedTasks: [],
1111
- initiatives: [{
1112
- id: 'initiative-1',
1113
- title: 'Revenue Expansion v2',
1114
- description: null,
1115
- createdAt: '2026-03-29T10:00:00.000Z',
1116
- updatedAt: '2026-03-29T11:05:00.000Z'
1117
- }],
1118
- workstreams: [{
1119
- id: 'workstream-1',
1120
- initiativeId: 'initiative-1',
1121
- title: 'Checkout UX',
1122
- description: null,
1123
- createdAt: '2026-03-29T10:30:00.000Z',
1124
- updatedAt: '2026-03-29T11:30:00.000Z'
1125
- }]
1126
- });
1127
- expect(changedSignature).not.toEqual(baseSignature);
1128
- });
1129
- it('changes when initiative or workstream attachments change', () => {
1130
- const baseSignature = buildWorkspaceSyncSignatureModel({
1131
- workspaceId: 'ws-1',
1132
- tasks: [],
1133
- archivedTasks: [],
1134
- initiatives: [{
1135
- id: 'initiative-1',
1136
- title: 'Revenue Expansion',
1137
- description: null,
1138
- createdAt: '2026-03-29T10:00:00.000Z',
1139
- updatedAt: '2026-03-29T11:00:00.000Z',
1140
- attachments: []
1141
- }],
1142
- workstreams: [{
1143
- id: 'workstream-1',
1144
- initiativeId: 'initiative-1',
1145
- title: 'Checkout UX',
1146
- description: null,
1147
- createdAt: '2026-03-29T10:30:00.000Z',
1148
- updatedAt: '2026-03-29T11:30:00.000Z',
1149
- attachments: []
1150
- }]
1151
- });
1152
- const changedSignature = buildWorkspaceSyncSignatureModel({
1153
- workspaceId: 'ws-1',
1154
- tasks: [],
1155
- archivedTasks: [],
1156
- initiatives: [{
1157
- id: 'initiative-1',
1158
- title: 'Revenue Expansion',
1159
- description: null,
1160
- createdAt: '2026-03-29T10:00:00.000Z',
1161
- updatedAt: '2026-03-29T11:00:00.000Z',
1162
- attachments: [{
1163
- path: 'workspaces/ws-1/assets/documents/asset-in.md',
1164
- assetId: 'asset-in',
1165
- referenceLabel: 'D-31',
1166
- caption: 'Revenue context'
1167
- }]
1168
- }],
1169
- workstreams: [{
1170
- id: 'workstream-1',
1171
- initiativeId: 'initiative-1',
1172
- title: 'Checkout UX',
1173
- description: null,
1174
- createdAt: '2026-03-29T10:30:00.000Z',
1175
- updatedAt: '2026-03-29T11:30:00.000Z',
1176
- attachments: [{
1177
- path: 'workspaces/ws-1/assets/documents/asset-ws.md',
1178
- assetId: 'asset-ws',
1179
- referenceLabel: 'D-32',
1180
- caption: 'Checkout context'
1181
- }]
1182
- }]
1183
- });
1184
- expect(changedSignature).not.toEqual(baseSignature);
1185
- });
1186
- it('changes when document review sessions change', () => {
1187
- const baseSignature = buildWorkspaceSyncSignatureModel({
1188
- workspaceId: 'ws-1',
1189
- tasks: [],
1190
- archivedTasks: [],
1191
- documentReviewSessions: [{
1192
- id: 'review-1',
1193
- assetId: 'asset-doc-1',
1194
- documentId: 'doc-1',
1195
- documentVersion: 1,
1196
- title: 'Review',
1197
- status: 'open',
1198
- comments: [],
1199
- createdByActorId: null,
1200
- updatedByActorId: null,
1201
- createdAt: '2026-03-24T10:00:00.000Z',
1202
- updatedAt: '2026-03-24T10:05:00.000Z',
1203
- deletedAt: null
1204
- }]
1205
- });
1206
- const changedSignature = buildWorkspaceSyncSignatureModel({
1207
- workspaceId: 'ws-1',
1208
- tasks: [],
1209
- archivedTasks: [],
1210
- documentReviewSessions: [{
1211
- id: 'review-1',
1212
- assetId: 'asset-doc-1',
1213
- documentId: 'doc-1',
1214
- documentVersion: 1,
1215
- title: 'Review',
1216
- status: 'resolved',
1217
- comments: [{
1218
- id: 'comment-1',
1219
- order: 0,
1220
- body: 'Updated',
1221
- anchor: null,
1222
- authorActorId: null,
1223
- createdAt: '2026-03-24T10:00:00.000Z',
1224
- updatedAt: '2026-03-24T10:05:00.000Z',
1225
- deletedAt: null
1226
- }],
1227
- createdByActorId: null,
1228
- updatedByActorId: null,
1229
- createdAt: '2026-03-24T10:00:00.000Z',
1230
- updatedAt: '2026-03-24T10:06:00.000Z',
1231
- deletedAt: null
1232
- }]
1233
- });
1234
- expect(changedSignature).not.toEqual(baseSignature);
1235
- });
1236
- it('changes when document review comments change', () => {
1237
- const baseSignature = buildWorkspaceSyncSignatureModel({
1238
- workspaceId: 'ws-1',
1239
- tasks: [],
1240
- archivedTasks: [],
1241
- documentReviewComments: [{
1242
- id: 'comment-1',
1243
- sessionId: 'review-1',
1244
- body: 'Original',
1245
- anchor: null,
1246
- order: 0,
1247
- authorActorId: null,
1248
- createdAt: '2026-03-24T10:00:00.000Z',
1249
- updatedAt: '2026-03-24T10:05:00.000Z',
1250
- deletedAt: null
1251
- }]
1252
- });
1253
- const changedSignature = buildWorkspaceSyncSignatureModel({
1254
- workspaceId: 'ws-1',
1255
- tasks: [],
1256
- archivedTasks: [],
1257
- documentReviewComments: [{
1258
- id: 'comment-1',
1259
- sessionId: 'review-1',
1260
- body: 'Updated',
1261
- anchor: null,
1262
- order: 0,
1263
- authorActorId: null,
1264
- createdAt: '2026-03-24T10:00:00.000Z',
1265
- updatedAt: '2026-03-24T10:06:00.000Z',
1266
- deletedAt: null
1267
- }]
1268
- });
1269
- expect(changedSignature).not.toEqual(baseSignature);
1270
- });
1271
- it('changes when task activity changes', () => {
1272
- const baseSignature = buildWorkspaceSyncSignatureModel({
1273
- workspaceId: 'ws-1',
1274
- tasks: [],
1275
- archivedTasks: [],
1276
- taskEvents: [{
1277
- id: 'task-event-1',
1278
- entityType: 'task',
1279
- entityId: 'task-1',
1280
- taskId: 'task-1',
1281
- workspaceId: 'ws-1',
1282
- action: 'task-status-changed',
1283
- actor: 'ai-profile-1',
1284
- actorType: 'ai',
1285
- createdAt: '2026-04-13T10:05:00.000Z'
1286
- }]
1287
- });
1288
- const changedSignature = buildWorkspaceSyncSignatureModel({
1289
- workspaceId: 'ws-1',
1290
- tasks: [],
1291
- archivedTasks: [],
1292
- taskEvents: [{
1293
- id: 'task-event-1',
1294
- entityType: 'task',
1295
- entityId: 'task-1',
1296
- taskId: 'task-1',
1297
- workspaceId: 'ws-1',
1298
- action: 'task-status-changed',
1299
- actor: 'ai-profile-1',
1300
- actorType: 'ai',
1301
- createdAt: '2026-04-13T10:06:00.000Z'
1302
- }]
1303
- });
1304
- expect(changedSignature).not.toEqual(baseSignature);
1305
- });
1306
- });
1307
- describe('dirty task watermarks', () => {
1308
- it('resends once when a legacy watermark-only task baseline exists', () => {
1309
- const task = {
1310
- id: 'task-1',
1311
- title: 'Task 1',
1312
- status: 'in-progress',
1313
- createdAt: '2026-03-16T10:00:00.000Z',
1314
- updatedAt: '2026-03-16T10:05:00.000Z'
1315
- };
1316
- const payload = buildWorkspaceSyncPayloadModel({
1317
- tasks: [task],
1318
- archivedTasks: [],
1319
- lastPushedTaskIds: new Set(['task-1']),
1320
- pendingDeletedTaskIds: new Set(),
1321
- lastPushedWatermarks: new Map([
1322
- ['task-1', '2026-03-16T10:00:00.000Z']
1323
- ])
1324
- });
1325
- expect(payload.changes).toEqual(expect.arrayContaining([
1326
- expect.objectContaining({
1327
- op: 'upsert',
1328
- archived: false,
1329
- task: expect.objectContaining({
1330
- id: 'task-1',
1331
- status: 'in-progress'
1332
- })
1333
- })
1334
- ]));
1335
- expect(payload.pushedWatermarks.get('task-1')).toBe('2026-03-16T10:05:00.000Z');
1336
- expect(payload.pushedTaskChangeKeys.get('task-1')).toBe(taskSyncResourceAdapter.getChangeKey({ ...task, isArchived: false }));
1337
- });
1338
- it('does not resend a task when only updatedAt changes but the replicated change key matches', () => {
1339
- const task = {
1340
- id: 'task-1',
1341
- title: 'Task 1',
1342
- status: 'in-progress',
1343
- createdAt: '2026-03-16T10:00:00.000Z',
1344
- updatedAt: '2026-03-16T10:05:00.000Z'
1345
- };
1346
- const payload = buildWorkspaceSyncPayloadModel({
1347
- tasks: [task],
1348
- archivedTasks: [],
1349
- lastPushedTaskIds: new Set(['task-1']),
1350
- pendingDeletedTaskIds: new Set(),
1351
- lastPushedWatermarks: new Map([
1352
- ['task-1', '2026-03-16T10:00:00.000Z']
1353
- ]),
1354
- lastPushedTaskChangeKeys: new Map([
1355
- ['task-1', taskSyncResourceAdapter.getChangeKey({ ...task, isArchived: false })]
1356
- ])
1357
- });
1358
- expect(payload.changes.some((change) => change.op === 'upsert' && change.task.id === 'task-1')).toBe(false);
1359
- expect(payload.currentTaskWatermarks.get('task-1')).toBe('2026-03-16T10:05:00.000Z');
1360
- });
1361
- it('treats comment and attachment changes as replicated task-state changes', () => {
1362
- const task = {
1363
- id: 'task-1',
1364
- title: 'Task 1',
1365
- status: 'task',
1366
- createdAt: '2026-03-16T10:00:00.000Z',
1367
- updatedAt: '2026-03-16T10:05:00.000Z',
1368
- comments: [{
1369
- id: 'comment-1',
1370
- author: 'ai-profile-1',
1371
- text: 'Shipped',
1372
- timestamp: '2026-03-16T10:05:00.000Z'
1373
- }],
1374
- attachments: [{
1375
- path: 'workspaces/ws-1/assets/files/asset-1.txt',
1376
- caption: 'Spec',
1377
- timestamp: '2026-03-16T10:05:00.000Z'
1378
- }]
1379
- };
1380
- const previousTask = {
1381
- ...task,
1382
- comments: [],
1383
- attachments: []
1384
- };
1385
- const payload = buildWorkspaceSyncPayloadModel({
1386
- tasks: [task],
1387
- archivedTasks: [],
1388
- lastPushedTaskIds: new Set(['task-1']),
1389
- pendingDeletedTaskIds: new Set(),
1390
- lastPushedWatermarks: new Map([
1391
- ['task-1', '2026-03-16T10:05:00.000Z']
1392
- ]),
1393
- lastPushedTaskChangeKeys: new Map([
1394
- ['task-1', taskSyncResourceAdapter.getChangeKey({ ...previousTask, isArchived: false })]
1395
- ])
1396
- });
1397
- expect(payload.changes).toEqual(expect.arrayContaining([
1398
- expect.objectContaining({
1399
- op: 'upsert',
1400
- task: expect.objectContaining({
1401
- id: 'task-1',
1402
- comments: expect.arrayContaining([
1403
- expect.objectContaining({ id: 'comment-1' })
1404
- ]),
1405
- attachments: expect.arrayContaining([
1406
- expect.objectContaining({ path: 'workspaces/ws-1/assets/files/asset-1.txt' })
1407
- ])
1408
- })
1409
- })
1410
- ]));
1411
- });
1412
- it('keeps provisional local-reference tasks dirty until a canonical reference is assigned', () => {
1413
- const payload = buildWorkspaceSyncPayloadModel({
1414
- tasks: [{
1415
- id: 'task-provisional-1',
1416
- title: 'Offline draft',
1417
- status: 'task',
1418
- createdAt: '2026-03-16T10:00:00.000Z',
1419
- updatedAt: '2026-03-16T10:05:00.000Z',
1420
- referenceNumber: null,
1421
- localReferenceNumber: 3
1422
- }],
1423
- archivedTasks: [],
1424
- lastPushedTaskIds: new Set(['task-provisional-1']),
1425
- pendingDeletedTaskIds: new Set(),
1426
- lastPushedWatermarks: new Map([
1427
- ['task-provisional-1', '2026-03-16T10:05:00.000Z']
1428
- ])
1429
- });
1430
- expect(payload.changes).toEqual(expect.arrayContaining([
1431
- expect.objectContaining({
1432
- op: 'upsert',
1433
- archived: false,
1434
- task: expect.objectContaining({
1435
- id: 'task-provisional-1',
1436
- referenceNumber: null
1437
- })
1438
- })
1439
- ]));
1440
- });
1441
- });
1442
- });