@cosmicdrift/kumiko-samples 0.389.0 → 0.390.0

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 (296) hide show
  1. package/package.json +1 -1
  2. package/packages/bundled-features/package.json +25 -13
  3. package/packages/bundled-features/src/audit/feature.ts +1 -1
  4. package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +1 -1
  5. package/packages/bundled-features/src/auth-email-password/bootstrap.ts +13 -1
  6. package/packages/bundled-features/src/auth-email-password/changes.json +6 -0
  7. package/packages/bundled-features/src/auth-email-password/feature.ts +2 -2
  8. package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +7 -7
  9. package/packages/bundled-features/src/auth-email-password/handlers/confirm-account-unlock.write.ts +3 -3
  10. package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
  11. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +9 -7
  12. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +11 -7
  13. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -4
  14. package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +2 -2
  15. package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +9 -7
  16. package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +23 -16
  17. package/packages/bundled-features/src/auth-email-password/handlers/reset-password.write.ts +3 -2
  18. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +12 -12
  19. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +12 -8
  20. package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +4 -2
  21. package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +2 -2
  22. package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +3 -2
  23. package/packages/bundled-features/src/auth-email-password/seeding.ts +14 -9
  24. package/packages/bundled-features/src/auth-foundation/feature.ts +13 -4
  25. package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +5 -5
  26. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +24 -14
  27. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +9 -3
  28. package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +6 -1
  29. package/packages/bundled-features/src/auth-mfa/handlers/enable-start.write.ts +3 -1
  30. package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +14 -9
  31. package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -4
  32. package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +25 -14
  33. package/packages/bundled-features/src/auth-mfa/mfa-code-verifier.ts +10 -4
  34. package/packages/bundled-features/src/auth-mfa/mfa-status-checker.ts +9 -3
  35. package/packages/bundled-features/src/auth-mfa/verify-factor.ts +1 -1
  36. package/packages/bundled-features/src/billing-foundation/billing-info-query.ts +2 -2
  37. package/packages/bundled-features/src/billing-foundation/changes.json +6 -0
  38. package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +1 -1
  39. package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +5 -5
  40. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +10 -7
  41. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +15 -12
  42. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-open-cancels.ts +22 -0
  43. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-provider-lock.ts +4 -0
  44. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +4 -4
  45. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +17 -58
  46. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +10 -8
  47. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +18 -15
  48. package/packages/bundled-features/src/billing-foundation/events.ts +1 -1
  49. package/packages/bundled-features/src/billing-foundation/feature.ts +8 -3
  50. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +1 -1
  51. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +5 -5
  52. package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +3 -3
  53. package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +2 -2
  54. package/packages/bundled-features/src/billing-foundation/projection.ts +1 -1
  55. package/packages/bundled-features/src/billing-foundation/types.ts +1 -1
  56. package/packages/bundled-features/src/cap-counter/aggregate-id.ts +1 -1
  57. package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +5 -4
  58. package/packages/bundled-features/src/cap-counter/cap-reservation.ts +1 -1
  59. package/packages/bundled-features/src/cap-counter/constants.ts +1 -1
  60. package/packages/bundled-features/src/cap-counter/feature.ts +2 -2
  61. package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +1 -1
  62. package/packages/bundled-features/src/cap-counter/reservation-chain.ts +1 -1
  63. package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +4 -4
  64. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -1
  65. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
  66. package/packages/bundled-features/src/compliance-profiles/seeding.ts +12 -7
  67. package/packages/bundled-features/src/config/feature.ts +6 -6
  68. package/packages/bundled-features/src/config/handlers/cascade.query.ts +1 -1
  69. package/packages/bundled-features/src/config/handlers/readiness.query.ts +1 -1
  70. package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +23 -13
  71. package/packages/bundled-features/src/config/handlers/reset.write.ts +3 -3
  72. package/packages/bundled-features/src/config/handlers/selected-extensions.query.ts +1 -1
  73. package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
  74. package/packages/bundled-features/src/config/handlers/values.query.ts +1 -1
  75. package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +8 -8
  76. package/packages/bundled-features/src/crypto-shredding/resolve-file-bytes-deleter.ts +3 -3
  77. package/packages/bundled-features/src/custom-fields/changes.json +6 -0
  78. package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
  79. package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +1 -1
  80. package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +1 -1
  81. package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
  82. package/packages/bundled-features/src/custom-fields/index.ts +4 -0
  83. package/packages/bundled-features/src/data-retention/feature.ts +1 -1
  84. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +12 -2
  85. package/packages/bundled-features/src/delivery/attempt-log.ts +1 -1
  86. package/packages/bundled-features/src/delivery/delivery-service.ts +6 -5
  87. package/packages/bundled-features/src/delivery/feature.ts +2 -2
  88. package/packages/bundled-features/src/delivery/jobs.ts +11 -11
  89. package/packages/bundled-features/src/delivery/testing.ts +1 -1
  90. package/packages/bundled-features/src/document-ingest-foundation/events.ts +1 -1
  91. package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +1 -1
  92. package/packages/bundled-features/src/feature-toggles/handlers/set.write.ts +1 -1
  93. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
  94. package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
  95. package/packages/bundled-features/src/file-derivatives/handlers/public-variant-by-file-ref.query.ts +5 -1
  96. package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +7 -5
  97. package/packages/bundled-features/src/file-foundation/feature.ts +1 -1
  98. package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -5
  99. package/packages/bundled-features/src/file-provider-s3/feature.ts +4 -1
  100. package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +8 -8
  101. package/packages/bundled-features/src/form-draft/feature.ts +1 -1
  102. package/packages/bundled-features/src/form-draft/handlers/cleanup.job.ts +22 -12
  103. package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +3 -3
  104. package/packages/bundled-features/src/form-draft/handlers/get.query.ts +1 -1
  105. package/packages/bundled-features/src/foundation-shared/config-helpers.ts +1 -1
  106. package/packages/bundled-features/src/inbound-mail-foundation/events.ts +1 -1
  107. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +1 -1
  108. package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +2 -2
  109. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
  110. package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +1 -1
  111. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +10 -10
  112. package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +1 -1
  113. package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +1 -1
  114. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +1 -1
  115. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +4 -1
  116. package/packages/bundled-features/src/jobs/changes.json +6 -0
  117. package/packages/bundled-features/src/jobs/events.ts +1 -1
  118. package/packages/bundled-features/src/jobs/handlers/consumer.write.ts +7 -4
  119. package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
  120. package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +11 -5
  121. package/packages/bundled-features/src/jobs/handlers/retention-cleanup.job.ts +1 -1
  122. package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
  123. package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +1 -1
  124. package/packages/bundled-features/src/jobs/handlers/trigger.write.ts +2 -2
  125. package/packages/bundled-features/src/jobs/job-run-logger.ts +139 -90
  126. package/packages/bundled-features/src/legal-pages/README.md +1 -1
  127. package/packages/bundled-features/src/legal-pages/lib/boot-check.ts +12 -8
  128. package/packages/bundled-features/src/mail-foundation/feature.ts +2 -2
  129. package/packages/bundled-features/src/mail-transport-smtp/changes.json +6 -0
  130. package/packages/bundled-features/src/mail-transport-smtp/feature.ts +4 -1
  131. package/packages/bundled-features/src/managed-pages/seeding.ts +13 -8
  132. package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +1 -1
  133. package/packages/bundled-features/src/personal-access-tokens/feature.ts +1 -1
  134. package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +2 -2
  135. package/packages/bundled-features/src/personal-access-tokens/handlers/revoke.write.ts +1 -1
  136. package/packages/bundled-features/src/rate-limiting/handlers/status.query.ts +2 -2
  137. package/packages/bundled-features/src/readiness/handlers/status.query.ts +4 -1
  138. package/packages/bundled-features/src/renderer-foundation/api.ts +3 -3
  139. package/packages/bundled-features/src/secrets/announce-secret-change.ts +4 -4
  140. package/packages/bundled-features/src/secrets/feature.ts +9 -9
  141. package/packages/bundled-features/src/secrets/handlers/delete.write.ts +4 -3
  142. package/packages/bundled-features/src/secrets/handlers/rotate.job.ts +13 -8
  143. package/packages/bundled-features/src/secrets/handlers/set.write.ts +4 -3
  144. package/packages/bundled-features/src/secrets/secrets-context.ts +16 -6
  145. package/packages/bundled-features/src/secrets/write-gate.ts +1 -1
  146. package/packages/bundled-features/src/seo/feature.ts +1 -1
  147. package/packages/bundled-features/src/sessions/changes.json +12 -0
  148. package/packages/bundled-features/src/sessions/feature.ts +1 -1
  149. package/packages/bundled-features/src/sessions/handlers/cleanup.job.ts +2 -2
  150. package/packages/bundled-features/src/sessions/handlers/detail.query.ts +5 -2
  151. package/packages/bundled-features/src/sessions/handlers/list.query.ts +5 -2
  152. package/packages/bundled-features/src/sessions/handlers/mine.query.ts +5 -2
  153. package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +3 -3
  154. package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +1 -1
  155. package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
  156. package/packages/bundled-features/src/sessions/screens.ts +8 -4
  157. package/packages/bundled-features/src/sessions/session-revoked-event.ts +1 -1
  158. package/packages/bundled-features/src/sessions/web/session-device-cell.tsx +10 -12
  159. package/packages/bundled-features/src/sessions/web/user-agent.ts +12 -0
  160. package/packages/bundled-features/src/shared/index.ts +15 -0
  161. package/packages/bundled-features/src/shared/purge-files-reaching-subject.ts +12 -2
  162. package/packages/bundled-features/src/shared/require-for-tenant.ts +7 -5
  163. package/packages/bundled-features/src/shared/stream-tenant-backfill.ts +201 -0
  164. package/packages/bundled-features/src/shared/stream-tenant-derived-state.ts +107 -0
  165. package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -1
  166. package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +1 -1
  167. package/packages/bundled-features/src/subscription-dodo/runtime.ts +7 -7
  168. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +5 -3
  169. package/packages/bundled-features/src/subscription-stripe/runtime.ts +7 -7
  170. package/packages/bundled-features/src/template-resolver/api.ts +3 -3
  171. package/packages/bundled-features/src/template-resolver/changes.json +18 -0
  172. package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +5 -4
  173. package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +2 -2
  174. package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +1 -1
  175. package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +2 -2
  176. package/packages/bundled-features/src/template-resolver/handlers/send-test-shared.ts +1 -1
  177. package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +2 -2
  178. package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +6 -2
  179. package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +4 -2
  180. package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +1 -1
  181. package/packages/bundled-features/src/template-resolver/seeding.ts +12 -2
  182. package/packages/bundled-features/src/template-resolver/store-template-resource.ts +18 -13
  183. package/packages/bundled-features/src/template-resolver/template-resources.ts +4 -4
  184. package/packages/bundled-features/src/template-resolver/testing.ts +2 -2
  185. package/packages/bundled-features/src/template-resolver/web/document-insert-fields.tsx +222 -0
  186. package/packages/bundled-features/src/template-resolver/web/document-insert-model.ts +164 -0
  187. package/packages/bundled-features/src/template-resolver/web/document-insert-panel.tsx +115 -0
  188. package/packages/bundled-features/src/template-resolver/web/document-tiptap-bubble.tsx +177 -0
  189. package/packages/bundled-features/src/template-resolver/web/document-tiptap-content.tsx +38 -0
  190. package/packages/bundled-features/src/template-resolver/web/document-tiptap-context.ts +119 -0
  191. package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +51 -1098
  192. package/packages/bundled-features/src/template-resolver/web/document-tiptap-helpers.ts +161 -0
  193. package/packages/bundled-features/src/template-resolver/web/document-tiptap-layout.tsx +83 -0
  194. package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar-groups.tsx +153 -0
  195. package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar.tsx +182 -0
  196. package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +18 -0
  197. package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +19 -6
  198. package/packages/bundled-features/src/template-resolver/web/use-document-tiptap-model.ts +138 -0
  199. package/packages/bundled-features/src/tenant/changes.json +19 -0
  200. package/packages/bundled-features/src/tenant/handlers/cancel-invitation.write.ts +1 -1
  201. package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
  202. package/packages/bundled-features/src/tenant/handlers/remove-member.write.ts +2 -2
  203. package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +1 -1
  204. package/packages/bundled-features/src/tenant/index.ts +5 -0
  205. package/packages/bundled-features/src/tenant/invited-membership.ts +6 -1
  206. package/packages/bundled-features/src/tenant/schema/tenant.ts +4 -1
  207. package/packages/bundled-features/src/tenant/seeding.ts +65 -62
  208. package/packages/bundled-features/src/tenant/stream-tenant-backfill.ts +115 -0
  209. package/packages/bundled-features/src/tenant/testing.ts +77 -0
  210. package/packages/bundled-features/src/tenant-handover/events.ts +1 -1
  211. package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
  212. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +5 -5
  213. package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +22 -10
  214. package/packages/bundled-features/src/tenant-lifecycle/handlers/request-destruction.write.ts +22 -11
  215. package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +23 -12
  216. package/packages/bundled-features/src/tenant-lifecycle/stages.ts +7 -3
  217. package/packages/bundled-features/src/tenant-lifecycle/testing.ts +9 -8
  218. package/packages/bundled-features/src/tier-engine/entity.ts +1 -1
  219. package/packages/bundled-features/src/tier-engine/feature.ts +8 -3
  220. package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +3 -3
  221. package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +7 -1
  222. package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +3 -3
  223. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +71 -97
  224. package/packages/bundled-features/src/user/handlers/find-for-auth.query.ts +2 -2
  225. package/packages/bundled-features/src/user/handlers/update-roles.ts +2 -2
  226. package/packages/bundled-features/src/user/index.ts +2 -0
  227. package/packages/bundled-features/src/user/invited-global-roles.ts +6 -1
  228. package/packages/bundled-features/src/user/principal-status.ts +1 -1
  229. package/packages/bundled-features/src/user/seeding.ts +24 -6
  230. package/packages/bundled-features/src/user/testing.ts +22 -0
  231. package/packages/bundled-features/src/user-data-rights/COMPLIANCE.md +1 -1
  232. package/packages/bundled-features/src/user-data-rights/README.md +1 -1
  233. package/packages/bundled-features/src/user-data-rights/audit-download.ts +12 -2
  234. package/packages/bundled-features/src/user-data-rights/changes.json +24 -0
  235. package/packages/bundled-features/src/user-data-rights/feature.ts +29 -24
  236. package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -1
  237. package/packages/bundled-features/src/user-data-rights/handlers/deletion-grace-period.ts +1 -1
  238. package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +2 -2
  239. package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +2 -2
  240. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +1 -1
  241. package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +4 -4
  242. package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +12 -6
  243. package/packages/bundled-features/src/user-data-rights/index.ts +2 -0
  244. package/packages/bundled-features/src/user-data-rights/lib/mail-transport-resolver.ts +1 -1
  245. package/packages/bundled-features/src/user-data-rights/lib/storage-provider-resolver.ts +1 -1
  246. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +6 -1
  247. package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +8 -2
  248. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +14 -3
  249. package/packages/bundled-features/src/user-data-rights/run-user-export.ts +11 -1
  250. package/packages/bundled-features/src/user-data-rights/web/constants.ts +10 -0
  251. package/packages/bundled-features/src/user-data-rights/web/index.ts +6 -1
  252. package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +8 -6
  253. package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +1 -1
  254. package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +2 -2
  255. package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +1 -1
  256. package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +9 -4
  257. package/packages/bundled-features/src/waitlist/legacy-migrate.ts +6 -1
  258. package/packages/bundled-features/src/waitlist/link-on-signup.ts +7 -1
  259. package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
  260. package/packages/bundled-features/src/workflow-runner/db/queries/due-runs.ts +1 -1
  261. package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -3
  262. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +2 -1
  263. package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
  264. package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +8 -8
  265. package/packages/bundled-features/src/workflow-runner/runner.ts +6 -4
  266. package/samples/apps/cap-billing-demo/src/feature.ts +19 -5
  267. package/samples/apps/config-demo/src/app/server.ts +1 -1
  268. package/samples/apps/styleguide/src/app/server.ts +2 -2
  269. package/samples/apps/use-all-bundled/feature-manifest.json +1 -1
  270. package/samples/apps/use-all-bundled/src/app/seed.ts +12 -2
  271. package/samples/apps/user-data-rights-demo/README.md +1 -1
  272. package/samples/apps/user-data-rights-demo/src/feature.ts +3 -2
  273. package/samples/recipes/api-worker-split/README.md +2 -1
  274. package/samples/recipes/api-worker-split/src/feature.ts +2 -1
  275. package/samples/recipes/cross-feature-events/README.md +5 -5
  276. package/samples/recipes/cross-feature-events/package.json +1 -1
  277. package/samples/recipes/cross-feature-events/src/feature.ts +4 -4
  278. package/samples/recipes/event-sourcing/README.md +5 -5
  279. package/samples/recipes/event-sourcing/package.json +1 -1
  280. package/samples/recipes/event-sourcing/src/feature.ts +15 -15
  281. package/samples/recipes/files-post-processing/README.md +1 -1
  282. package/samples/recipes/files-post-processing/package.json +1 -1
  283. package/samples/recipes/files-post-processing/src/feature.ts +6 -4
  284. package/samples/recipes/lane-routing/package.json +1 -1
  285. package/samples/recipes/legal-pages/README.md +2 -2
  286. package/samples/recipes/lifecycle-hooks/README.md +2 -2
  287. package/samples/recipes/lifecycle-hooks/src/feature.ts +1 -1
  288. package/samples/recipes/managed-config/README.md +3 -3
  289. package/samples/recipes/search/README.md +1 -1
  290. package/samples/recipes/search/src/feature.ts +3 -3
  291. package/samples/recipes/secrets-demo/README.md +1 -1
  292. package/samples/recipes/secrets-demo/src/feature.ts +5 -2
  293. package/samples/recipes/state-machine/src/events.ts +1 -1
  294. package/samples/recipes/state-machine/src/reducer.ts +1 -1
  295. package/samples/recipes/workflow-engine/src/resume-loop.ts +4 -4
  296. package/samples/recipes/workflow-engine/src/workflow-runner.ts +2 -2
@@ -12,7 +12,7 @@ import { updateUserLifecycle } from "../lib/update-user-lifecycle.ts";
12
12
 
13
13
  // POST /api/user/restrict — GDPR Art. 18 account freeze.
14
14
  // Flips status=Active → Restricted and revokes all live sessions
15
- // for the user via cross-feature ctx.writeAs(sessions.revokeAllForUser).
15
+ // for the user via cross-feature ctx.dispatch.writeAs(sessions.revokeAllForUser).
16
16
  //
17
17
  // Write API behaviour:
18
18
  // - Login is blocked: login.write.ts checks status=Restricted (Atom 3).
@@ -53,7 +53,7 @@ export const restrictAccountWrite = defineWriteHandler({
53
53
  },
54
54
  escapeHatch: {
55
55
  reason:
56
- "Revokes all live sessions of the (possibly self-)restricted user via ctx.writeAs(SYSTEM, " +
56
+ "Revokes all live sessions of the (possibly self-)restricted user via ctx.dispatch.writeAs(SYSTEM, " +
57
57
  "sessions:write:user-session:revoke-all-for-user) — that handler's own access gate needs " +
58
58
  "privileged roles the acting caller doesn't necessarily have. It also checks the target " +
59
59
  "user's membership in the admin's tenant and appends the user lifecycle status change on " +
@@ -112,10 +112,10 @@ export const restrictAccountWrite = defineWriteHandler({
112
112
 
113
113
  // Cross-feature: all live sessions are revoked; otherwise the user could
114
114
  // continue writing with an existing JWT until token expiry.
115
- // ctx.writeAs(systemUser, ...) gives the privileged handler the
115
+ // ctx.dispatch.writeAs(systemUser, ...) gives the privileged handler the
116
116
  // System user roles required by its access gate.
117
117
  const systemUser = createSystemUser(event.user.tenantId);
118
- await ctx.writeAs(systemUser, "sessions:write:user-session:revoke-all-for-user", {
118
+ await ctx.dispatch.writeAs(systemUser, "sessions:write:user-session:revoke-all-for-user", {
119
119
  userId: targetUserId,
120
120
  });
121
121
 
@@ -17,7 +17,11 @@ import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
17
17
  import * as z from "zod";
18
18
  import { resolveAppTenantModel } from "../lib/resolve-tenant-model.ts";
19
19
  import { makeTenantStorageProviderResolver } from "../lib/storage-provider-resolver.ts";
20
- import { runForgetCleanup, type SendDeletionExecutedEmailFn } from "../run-forget-cleanup.ts";
20
+ import {
21
+ FORGET_CLEANUP_HANDLER_NAME,
22
+ runForgetCleanup,
23
+ type SendDeletionExecutedEmailFn,
24
+ } from "../run-forget-cleanup.ts";
21
25
 
22
26
  export type RunForgetCleanupOptions = {
23
27
  readonly sendDeletionExecutedEmail?: SendDeletionExecutedEmailFn;
@@ -56,7 +60,7 @@ export function createRunForgetCleanupHandler(opts: RunForgetCleanupOptions = {}
56
60
  const forgetDb = runner as DbConnection; // @cast-boundary db-runner — DbRunner narrows to DbConnection, config reads tolerate the outer tx
57
61
  const tenantModel = await resolveAppTenantModel({
58
62
  registry: ctx.registry,
59
- configResolver: ctx.configResolver,
63
+ configResolver: ctx.services.configResolver,
60
64
  db: forgetDb,
61
65
  userId: ctx.user.id,
62
66
  });
@@ -65,14 +69,16 @@ export function createRunForgetCleanupHandler(opts: RunForgetCleanupOptions = {}
65
69
  registry: ctx.registry,
66
70
  now: T.Now.instant(),
67
71
  tenantModel,
68
- ...(ctx.searchAdapter !== undefined && { searchAdapter: ctx.searchAdapter }),
72
+ ...(ctx.services.searchAdapter !== undefined && {
73
+ searchAdapter: ctx.services.searchAdapter,
74
+ }),
69
75
  buildStorageProvider: makeTenantStorageProviderResolver({
70
76
  registry: ctx.registry,
71
- configResolver: ctx.configResolver,
72
- secrets: ctx.secrets,
77
+ configResolver: ctx.services.configResolver,
78
+ secrets: ctx.services.secrets,
73
79
  db: forgetDb,
74
80
  userId: ctx.user.id,
75
- handlerName: "user-data-rights:run-forget-cleanup",
81
+ handlerName: FORGET_CLEANUP_HANDLER_NAME,
76
82
  }),
77
83
  ...(opts.sendDeletionExecutedEmail && {
78
84
  sendDeletionExecutedEmail: opts.sendDeletionExecutedEmail,
@@ -35,5 +35,7 @@ export {
35
35
  TENANT_MODEL_CONFIG_KEY,
36
36
  USER_DATA_RIGHTS_FEATURE,
37
37
  UserDataRightsHandlers,
38
+ UserDataRightsJobs,
38
39
  UserDataRightsQueries,
40
+ UserDataRightsScreens,
39
41
  } from "./web/constants.ts";
@@ -31,7 +31,7 @@ export function makeTenantMailTransportResolver(
31
31
  return async (tenantId) => {
32
32
  if (!ctx.configResolver) {
33
33
  throw new Error(
34
- `${ctx.handlerName}: ctx.configResolver missing — cannot resolve the mail transport for tenant ${tenantId}`,
34
+ `${ctx.handlerName}: ctx.services.configResolver missing — cannot resolve the mail transport for tenant ${tenantId}`,
35
35
  );
36
36
  }
37
37
  const config = createConfigAccessor(
@@ -30,7 +30,7 @@ export function makeTenantStorageProviderResolver(
30
30
  return async (tenantId) => {
31
31
  if (!ctx.configResolver) {
32
32
  throw new InternalError({
33
- message: `[${ctx.handlerName}] ctx.configResolver missing — cannot resolve the file provider for tenant ${tenantId}`,
33
+ message: `[${ctx.handlerName}] ctx.services.configResolver missing — cannot resolve the file provider for tenant ${tenantId}`,
34
34
  });
35
35
  }
36
36
  const config = createConfigAccessor(
@@ -62,7 +62,12 @@ export async function updateUserLifecycle(
62
62
  // (restrict/lift-restriction/grace-period/cancel/forget) that holds no row
63
63
  // version to pass as `payload.version`, which update() requires otherwise.
64
64
  // Callers needing a guard use `options.expect` instead.
65
- const tenantDb = createTenantDb({ db: conn, tenantId: SYSTEM_TENANT_ID, mode: "system" });
65
+ const tenantDb = createTenantDb({
66
+ db: conn,
67
+ tenantId: SYSTEM_TENANT_ID,
68
+ mode: "system",
69
+ reason: "User is tenant-agnostic, lifecycle transitions run in the system tenant",
70
+ });
66
71
  const result = await userExecutor.update(
67
72
  { id: userId, changes },
68
73
  createSystemUser(SYSTEM_TENANT_ID),
@@ -54,7 +54,7 @@ import { generateDownloadToken } from "./token-helpers.ts";
54
54
  type Instant = InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
55
55
 
56
56
  // Gauge short name (feature.ts registers it via r.metric + resolves ctx.meter
57
- // into a MetricsHandle — jobs get no bound ctx.metrics, see feature.ts).
57
+ // into a MetricsHandle — jobs get no bound ctx.obs.metrics, see feature.ts).
58
58
  // No "_seconds"/"_total" suffix: validateMetricName rejects both on a gauge.
59
59
  // Unit lives in the r.metric() `unit: "seconds"` declaration instead.
60
60
  export const EXPORT_CLEANUP_BACKLOG_AGE_METRIC = "export_cleanup_backlog_age";
@@ -727,7 +727,13 @@ function reportCleanupLastRun(metrics: MetricsHandle | undefined, now: Instant):
727
727
  // `requestedFromTenantId` preserves stream identity and keeps the stream counter consistent.
728
728
  // @wrapper-known semantic-alias
729
729
  function systemTenantDb(db: DbConnection, tenantId: TenantId) {
730
- return createTenantDb({ db, tenantId, mode: "system" });
730
+ return createTenantDb({
731
+ db,
732
+ tenantId,
733
+ mode: "system",
734
+ reason:
735
+ "export jobs run per user across tenants, requestedFromTenantId keeps the stream identity",
736
+ });
731
737
  }
732
738
 
733
739
  function buildExportStorageKey(job: JobRow): string {
@@ -56,6 +56,7 @@ import {
56
56
  type UserDataDeleteStrategy,
57
57
  type UserDataStorageProvider,
58
58
  } from "@cosmicdrift/kumiko-framework/engine";
59
+ import { runInSystemRowLevelSecurityScope } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
59
60
  import {
60
61
  createEscapeHatchReporter,
61
62
  UNATTRIBUTED_ACTOR,
@@ -243,13 +244,23 @@ const HOOK_ORDER_DEFAULT = EXT_USER_DATA_ORDER.DEFAULT;
243
244
  // console.warn so a crafted reason can't forge extra log lines, and cap the
244
245
  // length against a log-flood. The unsanitized reason still lands in the
245
246
  // returned `incomplete` entry untouched.
247
+ // Secret-read audit discriminator, not a QN; the value must stay stable for the audit trail.
248
+ export const FORGET_CLEANUP_HANDLER_NAME = "user-data-rights:run-forget-cleanup" as const;
249
+
246
250
  export function sanitizeReasonForLog(reason: string): string {
247
251
  return reason.replace(/[\r\n]+/g, " ").slice(0, 500);
248
252
  }
249
253
 
250
- export async function runForgetCleanup(
251
- args: RunForgetCleanupArgs,
252
- ): Promise<RunForgetCleanupResult> {
254
+ // Cross-tenant by design: the due-user and membership reads on the given runner and raw hook
255
+ // statements must see every tenant, whatever RLS scope the caller (job, operator handler) runs in.
256
+ export function runForgetCleanup(args: RunForgetCleanupArgs): Promise<RunForgetCleanupResult> {
257
+ return runInSystemRowLevelSecurityScope(
258
+ "forget cleanup anonymizes users across all tenants",
259
+ () => forgetDueUsers(args),
260
+ );
261
+ }
262
+
263
+ async function forgetDueUsers(args: RunForgetCleanupArgs): Promise<RunForgetCleanupResult> {
253
264
  const { db, registry, now, sendDeletionExecutedEmail, buildStorageProvider } = args;
254
265
  const kms = args.kms ?? configuredPiiSubjectKms();
255
266
  const appTenantModel: TenantUserModel = args.tenantModel ?? "multi-user";
@@ -43,6 +43,7 @@ import {
43
43
  type UserDataExportHook,
44
44
  type UserDataExportSnippet,
45
45
  } from "@cosmicdrift/kumiko-framework/engine";
46
+ import { runInSystemRowLevelSecurityScope } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
46
47
  import {
47
48
  createEscapeHatchReporter,
48
49
  UNATTRIBUTED_ACTOR,
@@ -163,7 +164,16 @@ function buildHookDb(
163
164
  * **Operator signal:** if bundle.json exceeds 100 MB in the ZIP, trigger
164
165
  * telemetry and evaluate a schema refactor.
165
166
  */
166
- export async function runUserExport(args: RunUserExportArgs): Promise<UserExportBundle> {
167
+ // Cross-tenant by design: the membership read on the given runner and raw hook statements
168
+ // must see every tenant of the user, whatever RLS scope the caller runs in.
169
+ export function runUserExport(args: RunUserExportArgs): Promise<UserExportBundle> {
170
+ return runInSystemRowLevelSecurityScope(
171
+ "user data export collects the subject's rows from all tenants",
172
+ () => exportUserData(args),
173
+ );
174
+ }
175
+
176
+ async function exportUserData(args: RunUserExportArgs): Promise<UserExportBundle> {
167
177
  const { db, registry, userId, now } = args;
168
178
 
169
179
  const memberships = await selectMany<{ tenantId: TenantId }>(db, tenantMembershipsTable, {
@@ -38,6 +38,16 @@ export const UserDataRightsQueries = {
38
38
  // `overrides.set(TENANT_MODEL_CONFIG_KEY, "single-user")`.
39
39
  export const TENANT_MODEL_CONFIG_KEY = "user-data-rights:config:tenant-model" as const;
40
40
 
41
+ export const UserDataRightsJobs = {
42
+ runExportJobs: "user-data-rights:job:run-export-jobs",
43
+ runForgetCleanup: "user-data-rights:job:run-forget-cleanup",
44
+ } as const;
45
+
46
+ export const UserDataRightsScreens = {
47
+ exportJobList: "user-data-rights:screen:export-job-list",
48
+ downloadAttemptList: "user-data-rights:screen:download-attempt-list",
49
+ } as const;
50
+
41
51
  export const UserDataRightsHandlers = {
42
52
  requestExport: "user-data-rights:write:request-export",
43
53
  requestDeletion: "user-data-rights:write:request-deletion",
@@ -7,7 +7,12 @@
7
7
  export { type UserDataRightsClientOptions, userDataRightsClient } from "./client-plugin.tsx";
8
8
  export type { ConfirmAccountDeletionScreenProps } from "./confirm-deletion-screen.tsx";
9
9
  export { ConfirmAccountDeletionScreen } from "./confirm-deletion-screen.tsx";
10
- export { UserDataRightsHandlers, UserDataRightsQueries } from "./constants.ts";
10
+ export {
11
+ UserDataRightsHandlers,
12
+ UserDataRightsJobs,
13
+ UserDataRightsQueries,
14
+ UserDataRightsScreens,
15
+ } from "./constants.ts";
11
16
  export { defaultTranslations } from "./default-translations.ts";
12
17
  export { formatDate } from "./privacy-center-screen.tsx";
13
18
  export { makePublicDeletionGate, type PublicDeletionRoutes } from "./public-deletion-gate.tsx";
@@ -24,7 +24,7 @@ function invalidCredentials() {
24
24
 
25
25
  // Change-email — authenticated, self-only. The user confirms with their
26
26
  // current password (re-authentication as with change-password); the new
27
- // address is written through the user-update handler via ctx.writeAs(system)
27
+ // address is written through the user-update handler via ctx.dispatch.writeAs(system)
28
28
  // because `email` is field-level privileged and would otherwise be silently
29
29
  // stripped during self-updates. emailVerified is set to false — the app then
30
30
  // triggers the verification flow (request-email-verification is public; the
@@ -45,8 +45,8 @@ export const changeEmailWrite = defineWriteHandler({
45
45
  escapeHatch: {
46
46
  grants: ["systemIdentity"],
47
47
  reason:
48
- "Reads the caller's own passwordHash via ctx.queryAs(SYSTEM, user:findForAuth), checks " +
49
- "email uniqueness the same way, and writes the new email via ctx.writeAs(SYSTEM, " +
48
+ "Reads the caller's own passwordHash via ctx.dispatch.queryAs(SYSTEM, user:findForAuth), checks " +
49
+ "email uniqueness the same way, and writes the new email via ctx.dispatch.writeAs(SYSTEM, " +
50
50
  "user:update) — field-access on passwordHash/email is privileged-only.",
51
51
  },
52
52
  // Payload carries the current password.
@@ -56,7 +56,7 @@ export const changeEmailWrite = defineWriteHandler({
56
56
  handler: async (event, ctx) => {
57
57
  const systemUser = createSystemUser(event.user.tenantId);
58
58
 
59
- const me = (await ctx.queryAs(systemUser, UserQueries.findForAuth, {
59
+ const me = (await ctx.dispatch.queryAs(systemUser, UserQueries.findForAuth, {
60
60
  id: event.user.id,
61
61
  })) as { id: string; email: string; passwordHash: string | null; version: number } | null; // @cast-boundary db-runner
62
62
 
@@ -79,7 +79,9 @@ export const changeEmailWrite = defineWriteHandler({
79
79
  );
80
80
  }
81
81
 
82
- const existing = await ctx.queryAs(systemUser, UserQueries.findForAuth, { email: newEmail });
82
+ const existing = await ctx.dispatch.queryAs(systemUser, UserQueries.findForAuth, {
83
+ email: newEmail,
84
+ });
83
85
  if (existing !== null) {
84
86
  return writeFailure(
85
87
  new UnprocessableError(UserErrors.emailAlreadyExists, {
@@ -93,7 +95,7 @@ export const changeEmailWrite = defineWriteHandler({
93
95
  // backfillUserStreamTenants.
94
96
  const writer = createSystemUser(SYSTEM_TENANT_ID);
95
97
 
96
- const writeRes = await ctx.writeAs(writer, UserHandlers.update, {
98
+ const writeRes = await ctx.dispatch.writeAs(writer, UserHandlers.update, {
97
99
  id: me.id,
98
100
  version: me.version,
99
101
  changes: { email: newEmail, emailVerified: false },
@@ -21,7 +21,7 @@ const PurgeSchema = z.object({ confirmCount: z.number().int().min(0) }).strict()
21
21
  const ExportSchema = z.object({}).strict();
22
22
 
23
23
  async function appendAudit(ctx: HandlerContext, type: string, count: number): Promise<void> {
24
- await ctx.unsafeAppendEvent({
24
+ await ctx.events.unsafeAppend({
25
25
  aggregateId: generateId(),
26
26
  aggregateType: WAITLIST_AUDIT_AGGREGATE_TYPE,
27
27
  type,
@@ -111,7 +111,7 @@ async function ensureOwnTenant(
111
111
 
112
112
  const newTenantId = parseTenantId(crypto.randomUUID());
113
113
  if (!newTenantId) throw new InternalError({ message: "waitlist:invite: tenant id mint failed" });
114
- const created = await ctx.writeAs(createSystemUser(newTenantId), TenantHandlers.create, {
114
+ const created = await ctx.dispatch.writeAs(createSystemUser(newTenantId), TenantHandlers.create, {
115
115
  id: newTenantId,
116
116
  key,
117
117
  name: entry.company ?? entry.name,
@@ -167,7 +167,7 @@ export function createInviteHandler(invite: WaitlistInviteOptions = DEFAULT_INVI
167
167
  // inviteCreate is admin-only (no system role); the system variant with an
168
168
  // empty globalRoles list is the only in-process path, and a waitlist
169
169
  // invite must never grant a global role.
170
- const invited = await ctx.writeAs(
170
+ const invited = await ctx.dispatch.writeAs(
171
171
  createSystemUser(target.tenantId),
172
172
  AuthHandlers.systemInviteCreate,
173
173
  {
@@ -64,7 +64,7 @@ async function revokeInvitation(
64
64
  db,
65
65
  { id, version },
66
66
  createSystemUser(tenantId),
67
- ctx.redis,
67
+ ctx.services.redis,
68
68
  );
69
69
  if (cancelled) return cancelled;
70
70
  }
@@ -83,7 +83,7 @@ async function hasOpenEntryForEmail(email: string, ctx: HandlerContext): Promise
83
83
  // resolver does not extend the lock. Postgres aborts the whole transaction after a failed
84
84
  // statement, even one whose JS error is caught here, so the resolver gets a ctx whose
85
85
  // systemDb is bound to its own savepoint: a failing statement rolls back only that.
86
- // Statements through any other ctx handle (ctx.query, ctx.write) stay in the handler transaction.
86
+ // Statements through any other ctx handle (ctx.dispatch.query, ctx.dispatch.write) stay in the handler transaction.
87
87
  async function resolveAdminRecipient(
88
88
  opts: WaitlistOptions,
89
89
  ctx: HandlerContext,
@@ -93,12 +93,17 @@ async function resolveAdminRecipient(
93
93
  try {
94
94
  return await runInSavepointIfSupported(systemDb.unsafeRaw(SUBMIT_LOCK_REASON), async (sp) => {
95
95
  const savepointSystemDb = createSystemDbView(
96
- createTenantDb({ db: sp, tenantId, mode: "system" }),
96
+ createTenantDb({
97
+ db: sp,
98
+ tenantId,
99
+ mode: "system",
100
+ reason: "waitlist entries are system-tenant rows submitted by an anonymous guest",
101
+ }),
97
102
  );
98
103
  return (await opts.notifyRecipient?.({ ...ctx, systemDb: savepointSystemDb })) || null;
99
104
  });
100
105
  } catch (err) {
101
- ctx.log?.warn("[waitlist:submit] admin notice recipient resolution failed", {
106
+ ctx.obs.log?.warn("[waitlist:submit] admin notice recipient resolution failed", {
102
107
  error: err instanceof Error ? err.message : String(err),
103
108
  });
104
109
  return null;
@@ -116,7 +121,7 @@ async function sendBestEffort(
116
121
  }
117
122
  await send(ctx.notify);
118
123
  } catch (err) {
119
- ctx.log?.warn(`[waitlist:submit] ${label} mail failed`, {
124
+ ctx.obs.log?.warn(`[waitlist:submit] ${label} mail failed`, {
120
125
  error: err instanceof Error ? err.message : String(err),
121
126
  });
122
127
  }
@@ -271,7 +271,12 @@ export async function migrateLegacyWaitlist(
271
271
  continue;
272
272
  }
273
273
  await transaction(db, async (tx) => {
274
- const tenantDb = createTenantDb({ db: tx, tenantId: SYSTEM_TENANT_ID, mode: "system" });
274
+ const tenantDb = createTenantDb({
275
+ db: tx,
276
+ tenantId: SYSTEM_TENANT_ID,
277
+ mode: "system",
278
+ reason: "legacy waitlist import writes system-tenant rows",
279
+ });
275
280
  const outcome = await importWaitlistEntry(tenantDb, entry, { dryRun: options.dryRun });
276
281
  if (outcome === "created") created += 1;
277
282
  else skipped += 1;
@@ -69,7 +69,13 @@ export const linkWaitlistOnSignup: MultiStreamApplyFn = async (event, tx) => {
69
69
  // skip: another membership came first.
70
70
  if (first && first.id !== event.aggregateId) return;
71
71
 
72
- const db = createTenantDb({ db: tx, tenantId: SYSTEM_TENANT_ID, mode: "system" });
72
+ const db = createTenantDb({
73
+ db: tx,
74
+ tenantId: SYSTEM_TENANT_ID,
75
+ mode: "system",
76
+ reason:
77
+ "linking a signup to its waitlist entry updates system-tenant rows from a membership event",
78
+ });
73
79
  const user = await fetchOne<{ email?: unknown }>(tx, userTable, { id: membership.userId });
74
80
  // skip: user row unreadable (erased or not projected yet).
75
81
  if (typeof user?.email !== "string") return;
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.390.0",
4
+ "type": "fix",
5
+ "title": "Woken workflow runs are due on the next resume tick",
6
+ "detail": "The event subscriber stamped wakeAt with the app clock, while the resume job selects due rows with the database clock. With an app clock ahead of the database, a woken row was not due yet and waited for a later tick. wakeAt is now set with the database clock, like the due-runs query."
7
+ },
2
8
  {
3
9
  "version": "0.370.0",
4
10
  "type": "breaking",
@@ -8,7 +8,7 @@ export type DueRow = { readonly runId: string; readonly stepIndex: number };
8
8
  export const DUE_RUNS_BATCH_SIZE = 100;
9
9
 
10
10
  // Dedup happens solely via resume-run's VersionConflictError-checked claim
11
- // (ctx.tryAppendEvent on WORKFLOW_RESUMED) — a second dispatch for the same
11
+ // (ctx.events.tryAppend on WORKFLOW_RESUMED) — a second dispatch for the same
12
12
  // row loses that race and no-ops. No row locks: in an autocommit statement
13
13
  // they would be released right after the SELECT and guard nothing.
14
14
  // `now()` stays the DB clock so job-host clock skew cannot mark rows due early.
@@ -23,7 +23,7 @@
23
23
  // existing resume-due-runs job picks the row up on its next
24
24
  // cron tick — the resume path remains unchanged.
25
25
 
26
- import { selectMany, updateMany } from "@cosmicdrift/kumiko-framework/db";
26
+ import { selectMany, sql, updateMany } from "@cosmicdrift/kumiko-framework/db";
27
27
  import type {
28
28
  FeatureRegistrar,
29
29
  MultiStreamApplyFn,
@@ -32,7 +32,6 @@ import type {
32
32
  } from "@cosmicdrift/kumiko-framework/engine";
33
33
  import { evaluateEventMatch } from "@cosmicdrift/kumiko-framework/engine";
34
34
  import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
35
- import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
36
35
  import type { TriggerEventRef } from "./runner.ts";
37
36
  import { workflowRunPendingTable } from "./tables.ts";
38
37
 
@@ -92,7 +91,9 @@ const wakeupApply: MultiStreamApplyFn = async (event, tx) => {
92
91
  aggregateId: event.aggregateId,
93
92
  version: event.version,
94
93
  } satisfies TriggerEventRef,
95
- wakeAt: getTemporal().Now.instant().toString(),
94
+ // DB clock, like the due comparison in due-runs.ts: an app-clock stamp ahead of the
95
+ // database clock would leave the row not-yet-due for the next resume tick.
96
+ wakeAt: sql`now()`,
96
97
  },
97
98
  { tenantId: row.tenantId, runId: row.runId, stepIndex: row.stepIndex },
98
99
  );
@@ -144,7 +144,8 @@ export function registerEventTrigger(r: FeatureRegistrar, workflow: WorkflowDefi
144
144
  version: event.version,
145
145
  },
146
146
  ...(idempotencyKey && { idempotencyKey }),
147
- handlerCtx: ctx,
147
+ // The MSP context is still flat; the runner reads the namespaced shape.
148
+ handlerCtx: { events: { unsafeAppend: ctx.unsafeAppendEvent } },
148
149
  });
149
150
  } catch (error) {
150
151
  // Failures outside the step loop (run-started append) have no step yet, so 0.
@@ -76,7 +76,7 @@ export function createWorkflowRunnerFeature() {
76
76
 
77
77
  for (const row of dueRows) {
78
78
  try {
79
- await ctx.write("workflow-runner:write:resume-run", {
79
+ await ctx.dispatch.write("workflow-runner:write:resume-run", {
80
80
  runId: row.runId,
81
81
  stepIndex: row.stepIndex,
82
82
  });
@@ -84,7 +84,7 @@ export function createWorkflowRunnerFeature() {
84
84
  // skip: one row's dispatch failing (infra hiccup, not a business
85
85
  // outcome — resume-run itself never throws for expected cases)
86
86
  // must not stop the rest of this tenant's due rows from resuming.
87
- ctx.log.warn(
87
+ ctx.obs.log.warn(
88
88
  `[workflow-runner:resume-due-runs] runId=${row.runId} stepIndex=${row.stepIndex} dispatch failed: ${String(error)}`,
89
89
  );
90
90
  }
@@ -7,7 +7,7 @@
7
7
  // 1. Fingerprint check FIRST — before claiming, before any pipeline
8
8
  // work. A changed workflow definition fails loud (WORKFLOW_RUN_FAILED,
9
9
  // reason "workflow_definition_changed"), never a silent skip.
10
- // 2. Claim via a savepoint-scoped WORKFLOW_RESUMED append (ctx.tryAppendEvent,
10
+ // 2. Claim via a savepoint-scoped WORKFLOW_RESUMED append (ctx.events.tryAppend,
11
11
  // not unsafeAppendEvent — a losing VersionConflict must not poison this
12
12
  // handler's own transaction). A losing claim means another worker beat
13
13
  // us to this row; silent no-op.
@@ -78,7 +78,7 @@ async function appendRunFailed(
78
78
  runId: string,
79
79
  failedPayload: WorkflowRunFailedPayload,
80
80
  ): Promise<void> {
81
- await ctx.unsafeAppendEvent({
81
+ await ctx.events.unsafeAppend({
82
82
  aggregateId: runId,
83
83
  aggregateType: WORKFLOW_AGGREGATE_TYPE,
84
84
  type: WORKFLOW_RUN_FAILED_TYPE,
@@ -176,7 +176,7 @@ function readResumedPayload(payload: unknown): {
176
176
  // mistake attempt 1's WORKFLOW_RESUMED for attempt 2's and skip the second
177
177
  // retry entirely.
178
178
  function isRunAlreadySettled(
179
- runEvents: Awaited<ReturnType<HandlerContext["loadAggregate"]>>,
179
+ runEvents: Awaited<ReturnType<HandlerContext["events"]["load"]>>,
180
180
  stepIndex: number,
181
181
  expectedRetryAttempt: number | undefined,
182
182
  ): boolean {
@@ -192,7 +192,7 @@ function isRunAlreadySettled(
192
192
  });
193
193
  }
194
194
 
195
- type LoadedEvents = Awaited<ReturnType<HandlerContext["loadAggregate"]>>;
195
+ type LoadedEvents = Awaited<ReturnType<HandlerContext["events"]["load"]>>;
196
196
 
197
197
  function isRecord(value: unknown): value is Record<string, unknown> {
198
198
  return typeof value === "object" && value !== null;
@@ -229,7 +229,7 @@ function readTriggerSource(startedPayload: unknown): TriggerSource | null {
229
229
  }
230
230
 
231
231
  async function loadReferencedEvent(ctx: HandlerContext, ref: TriggerEventRef) {
232
- const events = await ctx.loadAggregate(ref.aggregateId, { atVersion: ref.version });
232
+ const events = await ctx.events.load(ref.aggregateId, { atVersion: ref.version });
233
233
  return events.find((e) => e.id === ref.eventId);
234
234
  }
235
235
 
@@ -385,7 +385,7 @@ async function runResumedPipeline(ctx: HandlerContext, input: ResumedPipelineInp
385
385
  workflowName,
386
386
  stepIndex: steps.length,
387
387
  };
388
- await ctx.unsafeAppendEvent({
388
+ await ctx.events.unsafeAppend({
389
389
  aggregateId: runId,
390
390
  aggregateType: WORKFLOW_AGGREGATE_TYPE,
391
391
  type: WORKFLOW_RUN_COMPLETED_TYPE,
@@ -448,7 +448,7 @@ export const resumeRunHandler: WriteHandlerDef = {
448
448
  }
449
449
 
450
450
  const { workflowName } = pending;
451
- const runEvents = await ctx.loadAggregate(runId);
451
+ const runEvents = await ctx.events.load(runId);
452
452
 
453
453
  // Before the workflow resolution: a racing tick on an already-settled run
454
454
  // must not append run-failed after run-completed when the definition has
@@ -469,7 +469,7 @@ export const resumeRunHandler: WriteHandlerDef = {
469
469
  }
470
470
  const workflow = resolved.workflow;
471
471
 
472
- const claim = await ctx.tryAppendEvent({
472
+ const claim = await ctx.events.tryAppend({
473
473
  aggregateId: runId,
474
474
  aggregateType: WORKFLOW_AGGREGATE_TYPE,
475
475
  type: WORKFLOW_RESUMED_TYPE,
@@ -75,7 +75,9 @@ export type WorkflowRunFailedPayload = {
75
75
  readonly reason?: string;
76
76
  };
77
77
 
78
- export type WorkflowRunContext = Pick<HandlerContext, "unsafeAppendEvent">;
78
+ export type WorkflowRunContext = {
79
+ readonly events: Pick<HandlerContext["events"], "unsafeAppend">;
80
+ };
79
81
 
80
82
  export class WorkflowSuspensionUnsupportedError extends Error {
81
83
  constructor(
@@ -116,7 +118,7 @@ export async function startAndRunWorkflow(args: {
116
118
  ...(args.idempotencyKey && { idempotencyKey: args.idempotencyKey }),
117
119
  };
118
120
 
119
- await args.handlerCtx.unsafeAppendEvent({
121
+ await args.handlerCtx.events.unsafeAppend({
120
122
  aggregateId: args.runId,
121
123
  aggregateType: WORKFLOW_AGGREGATE_TYPE,
122
124
  type: WORKFLOW_RUN_STARTED_TYPE,
@@ -127,7 +129,7 @@ export async function startAndRunWorkflow(args: {
127
129
 
128
130
  // @cast-boundary msp-to-handler-ctx — event-trigger hands over the narrower
129
131
  // MultiStreamApplyContext (no query/write/writeAs). The step vocabulary this
130
- // runner drives (wait, compute, return) only ever calls unsafeAppendEvent.
132
+ // runner drives (wait, compute, return) only ever calls events.unsafeAppend.
131
133
  const stepCtx = args.handlerCtx as unknown as HandlerContext;
132
134
  const outcome = await runStepList(
133
135
  steps,
@@ -158,7 +160,7 @@ export async function startAndRunWorkflow(args: {
158
160
  stepIndex: steps.length,
159
161
  };
160
162
 
161
- await args.handlerCtx.unsafeAppendEvent({
163
+ await args.handlerCtx.events.unsafeAppend({
162
164
  aggregateId: args.runId,
163
165
  aggregateType: WORKFLOW_AGGREGATE_TYPE,
164
166
  type: WORKFLOW_RUN_COMPLETED_TYPE,