core-framework 3.1.0__tar.gz → 3.2.0__tar.gz

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 (809) hide show
  1. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/error-boundary.mdc +1 -1
  2. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/sync-host-rules/SKILL.md +8 -0
  3. {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/_deploy.yml +5 -0
  4. {core_framework-3.1.0 → core_framework-3.2.0}/.gitignore +1 -0
  5. {core_framework-3.1.0 → core_framework-3.2.0}/CHANGELOG.md +32 -1
  6. {core_framework-3.1.0 → core_framework-3.2.0}/PKG-INFO +2 -1
  7. {core_framework-3.1.0 → core_framework-3.2.0}/config.toml +8 -0
  8. {core_framework-3.1.0 → core_framework-3.2.0}/config.toml.template +8 -0
  9. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/bootstrap.py +4 -0
  10. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/aggregation_service.py +7 -3
  11. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/public_service.py +10 -22
  12. core_framework-3.2.0/core_framework/application/email/__init__.py +29 -0
  13. core_framework-3.2.0/core_framework/application/email/account_deletion_scheduled.py +77 -0
  14. core_framework-3.2.0/core_framework/application/email/enqueue.py +36 -0
  15. core_framework-3.2.0/core_framework/application/email/registry.py +64 -0
  16. core_framework-3.2.0/core_framework/application/email/send.py +52 -0
  17. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/event_token.py +4 -7
  18. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/attachment_service.py +14 -6
  19. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/avatar_service.py +3 -3
  20. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/banner_service.py +3 -3
  21. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/user_service.py +6 -6
  22. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/notification_service.py +40 -26
  23. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/polling_service.py +1 -1
  24. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/aggregation_service.py +8 -4
  25. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/public_service.py +10 -23
  26. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/enums.py +2 -0
  27. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/exceptions.py +14 -0
  28. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/admin_service.py +8 -8
  29. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/aggregation_service.py +1 -1
  30. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/authenticated_service.py +24 -9
  31. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/public_service.py +13 -3
  32. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/user_deletion_service.py +4 -4
  33. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/cache.py +23 -6
  34. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exception_handlers/application.py +12 -0
  35. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exception_handlers/common.py +2 -0
  36. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/middleware.py +1 -1
  37. core_framework-3.2.0/core_framework/core/process.py +89 -0
  38. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/settings.py +55 -0
  39. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/README.md +1 -1
  40. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/repository.py +15 -9
  41. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/service.py +9 -9
  42. core_framework-3.2.0/core_framework/domains/email/README.md +74 -0
  43. core_framework-3.2.0/core_framework/domains/email/__init__.py +7 -0
  44. core_framework-3.2.0/core_framework/domains/email/dependencies.py +91 -0
  45. core_framework-3.2.0/core_framework/domains/email/exceptions.py +10 -0
  46. core_framework-3.2.0/core_framework/domains/email/models.py +3 -0
  47. core_framework-3.2.0/core_framework/domains/email/ports/send.py +29 -0
  48. core_framework-3.2.0/core_framework/domains/email/service.py +98 -0
  49. core_framework-3.2.0/core_framework/domains/media/attachment/components/variant_encoder.py +48 -0
  50. core_framework-3.2.0/core_framework/domains/media/attachment/ports/variant_encoder.py +42 -0
  51. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/service.py +23 -3
  52. core_framework-3.2.0/core_framework/domains/media/avatar/components/variant_encoder.py +27 -0
  53. core_framework-3.2.0/core_framework/domains/media/avatar/ports/variant_encoder.py +24 -0
  54. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/service.py +6 -0
  55. core_framework-3.2.0/core_framework/domains/media/banner/components/variant_encoder.py +27 -0
  56. core_framework-3.2.0/core_framework/domains/media/banner/ports/variant_encoder.py +24 -0
  57. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/service.py +6 -0
  58. core_framework-3.2.0/core_framework/domains/media/shared/constants.py +5 -0
  59. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/repository.py +10 -6
  60. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/service.py +6 -6
  61. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/repository.py +16 -9
  62. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/service.py +9 -9
  63. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/README.md +2 -2
  64. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/__init__.py +2 -0
  65. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/outcomes.py +5 -0
  66. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/repository.py +65 -30
  67. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/service.py +23 -5
  68. core_framework-3.2.0/core_framework/infrastructure/email/resend_email_adapter.py +64 -0
  69. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/pillow_attachment_variant_encoder_adapter.py +82 -20
  70. core_framework-3.2.0/core_framework/infrastructure/media/pillow_avatar_variant_encoder_adapter.py +74 -0
  71. core_framework-3.2.0/core_framework/infrastructure/media/pillow_banner_variant_encoder_adapter.py +64 -0
  72. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/main.py +15 -1
  73. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/__init__.py +18 -0
  74. core_framework-3.2.0/core_framework/worker/tasks/process_account_deletion_scheduled_email.py +17 -0
  75. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_attachment_asset_variants.py +10 -0
  76. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_banner_asset_variants.py +11 -0
  77. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_media_asset_variants.py +11 -0
  78. core_framework-3.2.0/core_framework/worker/tasks/process_transactional_email.py +21 -0
  79. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/app/docker-compose.yml +3 -0
  80. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/deploy-playbook.md +11 -1
  81. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/README.md +12 -11
  82. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/cache-invalidation-design.md +10 -3
  83. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/create-idempotency-design.md +16 -16
  84. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/event-outbox-design.md +11 -11
  85. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/events-ingest-design.md +1 -1
  86. core_framework-3.2.0/docs/design/transactional-email-design.md +616 -0
  87. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/auth/registration.md +1 -1
  88. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/cache/admin_cache_invalidation.md +3 -3
  89. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/comment_stats_aggregation.md +1 -0
  90. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/create_comment.md +4 -4
  91. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/events/events.md +1 -1
  92. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/attachment_upload.md +3 -1
  93. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/avatar-upload-design.md +7 -7
  94. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/post-comment-attachments-design.md +35 -35
  95. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/upload-pipeline-design.md +7 -7
  96. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/upload_pipeline.md +4 -0
  97. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/mentions/mentions_in_content.md +2 -2
  98. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/create_post.md +4 -3
  99. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/post_stats_aggregation.md +1 -1
  100. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/account_deletion.md +20 -3
  101. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/admin_users.md +1 -1
  102. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/check_username_exists.md +1 -1
  103. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/follow-system-design.md +1 -1
  104. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/follow.md +2 -2
  105. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/profile.md +3 -2
  106. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/public_profile.md +10 -8
  107. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/user_stats_aggregation.md +1 -1
  108. {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/capability-wiring.md +43 -0
  109. {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/package-api.md +62 -4
  110. core_framework-3.2.0/docs/library/workers.md +105 -0
  111. {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/architecture-decisions.md +19 -1
  112. {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/client-error-visibility.md +4 -1
  113. {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/conventions.md +3 -2
  114. {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/domain-services-and-adapters.md +11 -0
  115. core_framework-3.2.0/docs/todo.md +5 -0
  116. {core_framework-3.1.0 → core_framework-3.2.0}/pyproject.toml +2 -1
  117. {core_framework-3.1.0 → core_framework-3.2.0}/tach.toml +8 -0
  118. core_framework-3.2.0/tests/integration/api/users/account_deletion_email_test.py +145 -0
  119. core_framework-3.2.0/tests/unit/application/email/account_deletion_scheduled_test.py +219 -0
  120. {core_framework-3.1.0 → core_framework-3.2.0}/uv.lock +21 -1
  121. core_framework-3.1.0/core_framework/domains/media/attachment/components/variant_encoder.py +0 -55
  122. core_framework-3.1.0/core_framework/domains/media/attachment/ports/variant_encoder.py +0 -36
  123. core_framework-3.1.0/core_framework/domains/media/avatar/components/variant_encoder.py +0 -41
  124. core_framework-3.1.0/core_framework/domains/media/avatar/ports/variant_encoder.py +0 -21
  125. core_framework-3.1.0/core_framework/domains/media/banner/components/variant_encoder.py +0 -41
  126. core_framework-3.1.0/core_framework/domains/media/banner/ports/variant_encoder.py +0 -21
  127. core_framework-3.1.0/core_framework/domains/media/shared/constants.py +0 -4
  128. core_framework-3.1.0/core_framework/infrastructure/media/pillow_avatar_variant_encoder_adapter.py +0 -52
  129. core_framework-3.1.0/core_framework/infrastructure/media/pillow_banner_variant_encoder_adapter.py +0 -35
  130. core_framework-3.1.0/docs/library/workers.md +0 -89
  131. core_framework-3.1.0/docs/todo.md +0 -4
  132. {core_framework-3.1.0 → core_framework-3.2.0}/.agents/skills/fastapi +0 -0
  133. {core_framework-3.1.0 → core_framework-3.2.0}/.agents/skills/library-skills/.library-skills.json +0 -0
  134. {core_framework-3.1.0 → core_framework-3.2.0}/.agents/skills/library-skills/SKILL.md +0 -0
  135. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/alembic-migrations.mdc +0 -0
  136. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/api-layer.mdc +0 -0
  137. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/api-reference-docs.mdc +0 -0
  138. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/api-validation.mdc +0 -0
  139. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/application-layer.mdc +0 -0
  140. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/constants-final.mdc +0 -0
  141. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/constitution.mdc +0 -0
  142. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/database-triggers.mdc +0 -0
  143. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/docstrings.mdc +0 -0
  144. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-caller-context.mdc +0 -0
  145. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-imports.mdc +0 -0
  146. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-input-guards.mdc +0 -0
  147. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-repository-exceptions.mdc +0 -0
  148. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-services-and-adapters.mdc +0 -0
  149. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/flow-documentation.mdc +0 -0
  150. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/inline-raise-messages.mdc +0 -0
  151. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/integration-test-strategy.mdc +0 -0
  152. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/layer-boundaries.mdc +0 -0
  153. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/no-code-in-docs.mdc +0 -0
  154. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/postgres-config-conventions.mdc +0 -0
  155. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/repository-schema-isolation.mdc +0 -0
  156. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/strong-reads.mdc +0 -0
  157. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/structured-logging.mdc +0 -0
  158. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/add-config/SKILL.md +0 -0
  159. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/add-domain/SKILL.md +0 -0
  160. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/code-review/SKILL.md +0 -0
  161. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/fastapi +0 -0
  162. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/implement-feature/SKILL.md +0 -0
  163. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/recommend-features/SKILL.md +0 -0
  164. {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/write-flow-doc/SKILL.md +0 -0
  165. {core_framework-3.1.0 → core_framework-3.2.0}/.dockerignore +0 -0
  166. {core_framework-3.1.0 → core_framework-3.2.0}/.github/ci/package-lock.json +0 -0
  167. {core_framework-3.1.0 → core_framework-3.2.0}/.github/ci/package.json +0 -0
  168. {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/dev-ci-cd.yaml +0 -0
  169. {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/manual-deployment.yaml +0 -0
  170. {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/manual-observability-deployment.yaml +0 -0
  171. {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/publish-pypi.yml +0 -0
  172. {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/test.yaml +0 -0
  173. {core_framework-3.1.0 → core_framework-3.2.0}/.pre-commit-config.yaml +0 -0
  174. {core_framework-3.1.0 → core_framework-3.2.0}/.python-version +0 -0
  175. {core_framework-3.1.0 → core_framework-3.2.0}/LICENSE +0 -0
  176. {core_framework-3.1.0 → core_framework-3.2.0}/README.md +0 -0
  177. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/README +0 -0
  178. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/env.py +0 -0
  179. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/script.py.mako +0 -0
  180. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/versions/2d61a5e88d76_analytics_utm_events_add_kind.py +0 -0
  181. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/versions/585105e1149e_analytics_add_utm_events.py +0 -0
  182. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/versions/5b9ac98bb150_analytics_daily_fact.py +0 -0
  183. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/versions/7f2a9c1d4e8b_analytics_daily_fact_day_index.py +0 -0
  184. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic.ini +0 -0
  185. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/README +0 -0
  186. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/env.py +0 -0
  187. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/script.py.mako +0 -0
  188. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_add_comment_attachments.py +0 -0
  189. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency.py +0 -0
  190. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency_created_at_index.py +0 -0
  191. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_add_comment_mentions.py +0 -0
  192. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_tombstone_status_model.py +0 -0
  193. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/v1_comment_init_baseline.py +0 -0
  194. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic.ini +0 -0
  195. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic/README +0 -0
  196. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic/env.py +0 -0
  197. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic/script.py.mako +0 -0
  198. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic/versions/v1_ext_init_baseline.py +0 -0
  199. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic.ini +0 -0
  200. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/README +0 -0
  201. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/env.py +0 -0
  202. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/script.py.mako +0 -0
  203. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/versions/media_add_attachment_staging_registry.py +0 -0
  204. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/versions/media_add_banner_staging_and_lease.py +0 -0
  205. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/versions/media_attachment_staging_registry_published_at.py +0 -0
  206. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/versions/v1_media_init_baseline.py +0 -0
  207. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic.ini +0 -0
  208. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/README +0 -0
  209. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/env.py +0 -0
  210. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/script.py.mako +0 -0
  211. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/versions/moderation_add_event_outbox.py +0 -0
  212. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/versions/moderation_add_event_outbox_dead_indexes.py +0 -0
  213. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/versions/moderation_add_event_outbox_pending_created_at_index.py +0 -0
  214. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +0 -0
  215. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic.ini +0 -0
  216. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/README +0 -0
  217. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/env.py +0 -0
  218. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/script.py.mako +0 -0
  219. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/versions/2fdd68777706_notification_read_at_retention_index.py +0 -0
  220. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/versions/v1_notif_init_baseline.py +0 -0
  221. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic.ini +0 -0
  222. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/README +0 -0
  223. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/env.py +0 -0
  224. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/script.py.mako +0 -0
  225. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_add_post_attachments.py +0 -0
  226. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_add_post_create_idempotency.py +0 -0
  227. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_add_post_create_idempotency_created_at_index.py +0 -0
  228. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_add_post_mentions.py +0 -0
  229. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_post_views_retention_high_water_mark.py +0 -0
  230. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_tombstone_status_model.py +0 -0
  231. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/v1_post_init_baseline.py +0 -0
  232. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic.ini +0 -0
  233. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/README +0 -0
  234. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/env.py +0 -0
  235. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/script.py.mako +0 -0
  236. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/44df70a45ba6_user_add_signup_events.py +0 -0
  237. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_avatar_ingest_sequences.py +0 -0
  238. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_banner_ingest_sequences.py +0 -0
  239. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_date_of_birth_to_profiles.py +0 -0
  240. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_event_outbox.py +0 -0
  241. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_event_outbox_dead_indexes.py +0 -0
  242. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_event_outbox_pending_created_at_index.py +0 -0
  243. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_reserve_sentinel_user_ids.py +0 -0
  244. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/v1_user_init_baseline.py +0 -0
  245. {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic.ini +0 -0
  246. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/__init__.py +0 -0
  247. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/__init__.py +0 -0
  248. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/__init__.py +0 -0
  249. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/analytics/__init__.py +0 -0
  250. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/analytics/router.py +0 -0
  251. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/analytics/schemas.py +0 -0
  252. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/cache/__init__.py +0 -0
  253. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/cache/router.py +0 -0
  254. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/cache/schemas.py +0 -0
  255. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/comments/__init__.py +0 -0
  256. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/comments/router.py +0 -0
  257. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/comments/schemas.py +0 -0
  258. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/moderation/__init__.py +0 -0
  259. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/moderation/router.py +0 -0
  260. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/moderation/schemas.py +0 -0
  261. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/outbox/__init__.py +0 -0
  262. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/outbox/router.py +0 -0
  263. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/outbox/schemas.py +0 -0
  264. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/posts/__init__.py +0 -0
  265. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/posts/router.py +0 -0
  266. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/posts/schemas.py +0 -0
  267. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/router.py +0 -0
  268. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/users/__init__.py +0 -0
  269. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/users/router.py +0 -0
  270. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/users/schemas.py +0 -0
  271. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/attachments/__init__.py +0 -0
  272. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/attachments/router.py +0 -0
  273. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/attachments/schemas.py +0 -0
  274. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/auth/__init__.py +0 -0
  275. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/auth/router.py +0 -0
  276. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/auth/schemas.py +0 -0
  277. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/__init__.py +0 -0
  278. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/authenticated/__init__.py +0 -0
  279. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/authenticated/mappers.py +0 -0
  280. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/authenticated/router.py +0 -0
  281. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/authenticated/schemas.py +0 -0
  282. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/public/__init__.py +0 -0
  283. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/public/router.py +0 -0
  284. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/public/schemas.py +0 -0
  285. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/router.py +0 -0
  286. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/schemas.py +0 -0
  287. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/constants.py +0 -0
  288. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/dependencies.py +0 -0
  289. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/events/__init__.py +0 -0
  290. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/events/router.py +0 -0
  291. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/events/schemas.py +0 -0
  292. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/idempotency/__init__.py +0 -0
  293. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/idempotency/dependencies.py +0 -0
  294. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/__init__.py +0 -0
  295. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/authenticated/__init__.py +0 -0
  296. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/authenticated/router.py +0 -0
  297. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/authenticated/schemas.py +0 -0
  298. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/router.py +0 -0
  299. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/__init__.py +0 -0
  300. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/authenticated/__init__.py +0 -0
  301. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/authenticated/mappers.py +0 -0
  302. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/authenticated/router.py +0 -0
  303. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/authenticated/schemas.py +0 -0
  304. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/public/__init__.py +0 -0
  305. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/public/router.py +0 -0
  306. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/public/schemas.py +0 -0
  307. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/router.py +0 -0
  308. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/schemas.py +0 -0
  309. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/router.py +0 -0
  310. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/schemas.py +0 -0
  311. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/system/__init__.py +0 -0
  312. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/system/router.py +0 -0
  313. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/upload.py +0 -0
  314. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/__init__.py +0 -0
  315. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/authenticated/__init__.py +0 -0
  316. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/authenticated/mappers.py +0 -0
  317. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/authenticated/router.py +0 -0
  318. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/authenticated/schemas.py +0 -0
  319. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/mappers.py +0 -0
  320. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/public/__init__.py +0 -0
  321. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/public/router.py +0 -0
  322. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/public/schemas.py +0 -0
  323. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/router.py +0 -0
  324. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/shared/__init__.py +0 -0
  325. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/shared/schemas.py +0 -0
  326. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/__init__.py +0 -0
  327. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/__init__.py +0 -0
  328. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/bucket_service.py +0 -0
  329. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/prune_service.py +0 -0
  330. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/read_service.py +0 -0
  331. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/refresh_service.py +0 -0
  332. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/registry.py +0 -0
  333. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/__init__.py +0 -0
  334. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/comment_created.py +0 -0
  335. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/post_created.py +0 -0
  336. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/post_viewed.py +0 -0
  337. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/user_registered.py +0 -0
  338. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/__init__.py +0 -0
  339. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/access_service.py +0 -0
  340. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/auth_service.py +0 -0
  341. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/models.py +0 -0
  342. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/token_verification.py +0 -0
  343. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/cache/__init__.py +0 -0
  344. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/cache/invalidate_service.py +0 -0
  345. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/cache/models.py +0 -0
  346. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/__init__.py +0 -0
  347. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/admin_service.py +0 -0
  348. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/authenticated_service.py +0 -0
  349. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/cleanup_service.py +0 -0
  350. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/README.md +0 -0
  351. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/__init__.py +0 -0
  352. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/event_service.py +0 -0
  353. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/models.py +0 -0
  354. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/utm_capture_kinds.py +0 -0
  355. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/README.md +0 -0
  356. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/__init__.py +0 -0
  357. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/attachment_read.py +0 -0
  358. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/ingest_guards.py +0 -0
  359. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/read_url_config.py +0 -0
  360. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/__init__.py +0 -0
  361. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/appeal_service.py +0 -0
  362. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/moderator_service.py +0 -0
  363. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/report_service.py +0 -0
  364. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/scheduled_service.py +0 -0
  365. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/README.md +0 -0
  366. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/__init__.py +0 -0
  367. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/enums.py +0 -0
  368. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/inbox_service.py +0 -0
  369. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/mute_service.py +0 -0
  370. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/prune_service.py +0 -0
  371. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/__init__.py +0 -0
  372. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/block_mirrors.py +0 -0
  373. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/events.py +0 -0
  374. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/exceptions.py +0 -0
  375. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/follow_mirrors.py +0 -0
  376. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/handlers.py +0 -0
  377. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/ops_service.py +0 -0
  378. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/processing_service.py +0 -0
  379. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/restriction_mirrors.py +0 -0
  380. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/__init__.py +0 -0
  381. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/admin_service.py +0 -0
  382. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/authenticated_service.py +0 -0
  383. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/cleanup_service.py +0 -0
  384. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/__init__.py +0 -0
  385. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/request_context.py +0 -0
  386. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/worker_jobs.py +0 -0
  387. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/__init__.py +0 -0
  388. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/deletion_audit.py +0 -0
  389. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/enums.py +0 -0
  390. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/scheduled_service.py +0 -0
  391. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/asgi.py +0 -0
  392. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/bundled_alembic.py +0 -0
  393. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/constants.py +0 -0
  394. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/__init__.py +0 -0
  395. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/context.py +0 -0
  396. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/database.py +0 -0
  397. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exception_handlers/__init__.py +0 -0
  398. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exception_handlers/setup.py +0 -0
  399. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exceptions.py +0 -0
  400. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/firebase.py +0 -0
  401. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/http_client.py +0 -0
  402. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/http_probes.py +0 -0
  403. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/logging.py +0 -0
  404. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/observability.py +0 -0
  405. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/pagination.py +0 -0
  406. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/redis.py +0 -0
  407. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/runtime.py +0 -0
  408. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/__init__.py +0 -0
  409. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/README.md +0 -0
  410. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/__init__.py +0 -0
  411. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/constants.py +0 -0
  412. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/dependencies.py +0 -0
  413. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/exceptions.py +0 -0
  414. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/models.py +0 -0
  415. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/ports/__init__.py +0 -0
  416. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/ports/activity_source.py +0 -0
  417. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/repository.py +0 -0
  418. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/service.py +0 -0
  419. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/README.md +0 -0
  420. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/__init__.py +0 -0
  421. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/dependencies.py +0 -0
  422. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/exceptions.py +0 -0
  423. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/models.py +0 -0
  424. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/ports/__init__.py +0 -0
  425. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/ports/auth.py +0 -0
  426. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/service.py +0 -0
  427. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/__init__.py +0 -0
  428. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/constants.py +0 -0
  429. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/dependencies.py +0 -0
  430. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/enums.py +0 -0
  431. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/exceptions.py +0 -0
  432. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/models.py +0 -0
  433. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/outcomes.py +0 -0
  434. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/exceptions.py +0 -0
  435. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/README.md +0 -0
  436. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/__init__.py +0 -0
  437. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/__init__.py +0 -0
  438. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/components/__init__.py +0 -0
  439. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/components/finals_publisher.py +0 -0
  440. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/components/staging.py +0 -0
  441. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/constants.py +0 -0
  442. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/exceptions.py +0 -0
  443. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/models.py +0 -0
  444. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/ports/__init__.py +0 -0
  445. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/ports/finals_publisher.py +0 -0
  446. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/ports/staging.py +0 -0
  447. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/__init__.py +0 -0
  448. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/components/__init__.py +0 -0
  449. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/components/finals_publisher.py +0 -0
  450. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/components/staging.py +0 -0
  451. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/constants.py +0 -0
  452. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/exceptions.py +0 -0
  453. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/models.py +0 -0
  454. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/ports/__init__.py +0 -0
  455. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/ports/finals_publisher.py +0 -0
  456. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/ports/staging.py +0 -0
  457. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/__init__.py +0 -0
  458. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/components/__init__.py +0 -0
  459. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/components/finals_publisher.py +0 -0
  460. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/components/staging.py +0 -0
  461. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/constants.py +0 -0
  462. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/exceptions.py +0 -0
  463. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/models.py +0 -0
  464. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/ports/__init__.py +0 -0
  465. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/ports/finals_publisher.py +0 -0
  466. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/ports/staging.py +0 -0
  467. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/dependencies.py +0 -0
  468. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/__init__.py +0 -0
  469. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/exceptions.py +0 -0
  470. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/outcomes.py +0 -0
  471. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/repository.py +0 -0
  472. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/service.py +0 -0
  473. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/utils.py +0 -0
  474. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/README.md +0 -0
  475. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/__init__.py +0 -0
  476. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/dependencies.py +0 -0
  477. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/enums.py +0 -0
  478. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/exceptions.py +0 -0
  479. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/models.py +0 -0
  480. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/outbox_repository.py +0 -0
  481. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/outbox_service.py +0 -0
  482. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/outcomes.py +0 -0
  483. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/README.md +0 -0
  484. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/__init__.py +0 -0
  485. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/dependencies.py +0 -0
  486. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/enums.py +0 -0
  487. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/exceptions.py +0 -0
  488. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/models.py +0 -0
  489. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/repository.py +0 -0
  490. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/service.py +0 -0
  491. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/README.md +0 -0
  492. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/__init__.py +0 -0
  493. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/dependencies.py +0 -0
  494. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/exceptions.py +0 -0
  495. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/models.py +0 -0
  496. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/ports/__init__.py +0 -0
  497. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/ports/admin.py +0 -0
  498. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/ports/consumer.py +0 -0
  499. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/ports/producer.py +0 -0
  500. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/service.py +0 -0
  501. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/README.md +0 -0
  502. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/__init__.py +0 -0
  503. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/constants.py +0 -0
  504. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/dependencies.py +0 -0
  505. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/enums.py +0 -0
  506. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/exceptions.py +0 -0
  507. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/models.py +0 -0
  508. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/outcomes.py +0 -0
  509. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/repository_sql.py +0 -0
  510. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/constants.py +0 -0
  511. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/dependencies.py +0 -0
  512. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/enums.py +0 -0
  513. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/exceptions.py +0 -0
  514. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/models.py +0 -0
  515. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/outbox_repository.py +0 -0
  516. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/outbox_service.py +0 -0
  517. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/utils.py +0 -0
  518. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/utils.py +0 -0
  519. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/__init__.py +0 -0
  520. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/firebase/__init__.py +0 -0
  521. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/firebase/firebase_auth_adapter.py +0 -0
  522. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/__init__.py +0 -0
  523. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/attachment_staging_display_geometry.py +0 -0
  524. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/local_attachment_staging_adapter.py +0 -0
  525. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/local_avatar_staging_adapter.py +0 -0
  526. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/local_banner_staging_adapter.py +0 -0
  527. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/pillow_staging_image.py +0 -0
  528. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/ssh_attachment_finals_publisher_adapter.py +0 -0
  529. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/ssh_banner_finals_publisher_adapter.py +0 -0
  530. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/ssh_finals_publisher_adapter.py +0 -0
  531. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/staging_paths.py +0 -0
  532. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/outbox/__init__.py +0 -0
  533. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/outbox/outbox_consumer_adapter.py +0 -0
  534. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/outbox/outbox_producer_adapter.py +0 -0
  535. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/main.py +0 -0
  536. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/migrate_cli.py +0 -0
  537. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/py.typed +0 -0
  538. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/__init__.py +0 -0
  539. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/arq.py +0 -0
  540. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/auth.py +0 -0
  541. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/config.py +0 -0
  542. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/containers.py +0 -0
  543. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/firebase.py +0 -0
  544. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/firebase_identity_toolkit.py +0 -0
  545. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/hookspecs.py +0 -0
  546. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/httpx_test_client.py +0 -0
  547. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/media.py +0 -0
  548. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/migrations.py +0 -0
  549. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/plugin.py +0 -0
  550. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/__init__.py +0 -0
  551. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/__init__.py +0 -0
  552. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/analytics_refresh_cron.py +0 -0
  553. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +0 -0
  554. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_aggregate_post_stats.py +0 -0
  555. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_aggregate_user_stats.py +0 -0
  556. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_expired_account_deletions.py +0 -0
  557. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_expired_mute_lifts.py +0 -0
  558. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_poll_outbox.py +0 -0
  559. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_prune_analytics_daily_facts.py +0 -0
  560. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_prune_post_views.py +0 -0
  561. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_prune_read_notifications.py +0 -0
  562. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_purge_expired_create_idempotency_keys.py +0 -0
  563. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_refresh_analytics.py +0 -0
  564. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_sweep_stale_attachment_staging.py +0 -0
  565. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_sweep_stale_avatar_staging.py +0 -0
  566. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_sweep_stale_banner_staging.py +0 -0
  567. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/delete_attachment_finals.py +0 -0
  568. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/delete_superseded_avatar_finals.py +0 -0
  569. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/delete_superseded_banner_finals.py +0 -0
  570. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_account_deletion.py +0 -0
  571. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_admin_user_deletion.py +0 -0
  572. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_aggregate_comment_stats.py +0 -0
  573. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_aggregate_post_stats.py +0 -0
  574. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_aggregate_user_stats.py +0 -0
  575. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_cache_pattern_invalidation.py +0 -0
  576. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_destructive_comment_delete.py +0 -0
  577. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_destructive_post_delete.py +0 -0
  578. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_mute_lift.py +0 -0
  579. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_outbox_row.py +0 -0
  580. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_refresh_analytics_bucket.py +0 -0
  581. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_user_content_deletion.py +0 -0
  582. {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/worker_context.py +0 -0
  583. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/README.md +0 -0
  584. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/app/docker-compose.dev.yaml +0 -0
  585. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/app/dockerfile +0 -0
  586. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/.env.example +0 -0
  587. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/README.md +0 -0
  588. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/docker-compose.yml +0 -0
  589. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/application/api-http.json +0 -0
  590. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/application/worker-jobs.json +0 -0
  591. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/infrastructure/node-exporter.json +0 -0
  592. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/infrastructure/postgres-exporter.json +0 -0
  593. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/infrastructure/redis-exporter.json +0 -0
  594. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/provisioning/dashboards/dashboards.yml +0 -0
  595. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/provisioning/datasources/datasources.yml +0 -0
  596. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/loki.yaml +0 -0
  597. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/otel-collector-config.yaml +0 -0
  598. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/prometheus.yml +0 -0
  599. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets/node/.gitkeep +0 -0
  600. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets/postgres/.gitkeep +0 -0
  601. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets/redis/.gitkeep +0 -0
  602. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets.example/README.md +0 -0
  603. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets.example/node.yml.example +0 -0
  604. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets.example/postgres.yml.example +0 -0
  605. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets.example/redis.yml.example +0 -0
  606. {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/tempo.yaml +0 -0
  607. {core_framework-3.1.0 → core_framework-3.2.0}/docs/README.md +0 -0
  608. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/README.md +0 -0
  609. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/e2e-smoke-tests-design.md +0 -0
  610. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/edge-rate-limits.md +0 -0
  611. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/edge-upload-limits.md +0 -0
  612. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/examples/Caddyfile.example +0 -0
  613. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/examples/Caddyfile.observability.example +0 -0
  614. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/examples/image-server-known-hosts.example +0 -0
  615. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/playbook/postgres-backup-playbook.md +0 -0
  616. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/playbook/py-spy-playbook.md +0 -0
  617. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/app-server-deploy-ssh.md +0 -0
  618. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/caddy-setup.md +0 -0
  619. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/cloudflare-setup.md +0 -0
  620. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/image-server-ssh.md +0 -0
  621. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/observability-lgtm-setup.md +0 -0
  622. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/postgres-backup-setup.md +0 -0
  623. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/postgres-setup.md +0 -0
  624. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/py-spy-setup.md +0 -0
  625. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/redis-setup.md +0 -0
  626. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/ubuntu-server-setup.md +0 -0
  627. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/postgres-backup-design.md +0 -0
  628. {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/release-playbook.md +0 -0
  629. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/analytics-rollup-design.md +0 -0
  630. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/media-storage-topology.md +0 -0
  631. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/observability-lgtm-design.md +0 -0
  632. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/outbox-admin-ops-design.md +0 -0
  633. {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/utm-events-design.md +0 -0
  634. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/README.md +0 -0
  635. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/analytics/admin_analytics_reads.md +0 -0
  636. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/auth/access_control.md +0 -0
  637. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/README.md +0 -0
  638. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/admin_comments.md +0 -0
  639. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/comment_like.md +0 -0
  640. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/comment_report.md +0 -0
  641. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/delete_comment.md +0 -0
  642. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/edit_comment.md +0 -0
  643. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/retrieve_comments.md +0 -0
  644. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/deletion/comments.md +0 -0
  645. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/deletion/overview.md +0 -0
  646. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/deletion/posts.md +0 -0
  647. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/deletion/users.md +0 -0
  648. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/README.md +0 -0
  649. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/banner-upload-design.md +0 -0
  650. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/README.md +0 -0
  651. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/appeals.md +0 -0
  652. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/internal_notes.md +0 -0
  653. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/moderator_actions.md +0 -0
  654. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/reports.md +0 -0
  655. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/restrictions.md +0 -0
  656. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/notifications/notification_inbox.md +0 -0
  657. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/outbox/admin_outbox_ops.md +0 -0
  658. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/README.md +0 -0
  659. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/admin_posts.md +0 -0
  660. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/author_context.md +0 -0
  661. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/delete_post.md +0 -0
  662. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/edit_post.md +0 -0
  663. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/hashtag_discovery.md +0 -0
  664. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/post_like.md +0 -0
  665. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/post_report.md +0 -0
  666. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/post_visibility.md +0 -0
  667. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/retrieve_posts.md +0 -0
  668. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/account.md +0 -0
  669. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/avatar.md +0 -0
  670. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/banner.md +0 -0
  671. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/blocks.md +0 -0
  672. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/change_history.md +0 -0
  673. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/my_posts_and_comments.md +0 -0
  674. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/preferences.md +0 -0
  675. {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/user_removal.md +0 -0
  676. {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/README.md +0 -0
  677. {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/api.md +0 -0
  678. {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/core-framework-migration.md +0 -0
  679. {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/overview.md +0 -0
  680. {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/testing-plugin-design.md +0 -0
  681. {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/README.md +0 -0
  682. {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/database-triggers.md +0 -0
  683. {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/layers-and-boundaries.md +0 -0
  684. {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/patch-update-typed-payload.md +0 -0
  685. {core_framework-3.1.0 → core_framework-3.2.0}/docs/reviews/README.md +0 -0
  686. {core_framework-3.1.0 → core_framework-3.2.0}/docs/reviews/log.md +0 -0
  687. {core_framework-3.1.0 → core_framework-3.2.0}/docs/reviews/review-playbook.md +0 -0
  688. {core_framework-3.1.0 → core_framework-3.2.0}/docs/reviews/slices.md +0 -0
  689. {core_framework-3.1.0 → core_framework-3.2.0}/docs/testing.md +0 -0
  690. {core_framework-3.1.0 → core_framework-3.2.0}/firebase_config.example.json +0 -0
  691. {core_framework-3.1.0 → core_framework-3.2.0}/makefile +0 -0
  692. {core_framework-3.1.0 → core_framework-3.2.0}/tests/__init__.py +0 -0
  693. {core_framework-3.1.0 → core_framework-3.2.0}/tests/conftest.py +0 -0
  694. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_auth.py +0 -0
  695. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_canaries.py +0 -0
  696. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_media.py +0 -0
  697. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_settings.py +0 -0
  698. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_upload_payloads.py +0 -0
  699. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/conftest.py +0 -0
  700. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/media/pipeline_test.py +0 -0
  701. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/smoke/canary_test.py +0 -0
  702. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/smoke/media_test.py +0 -0
  703. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/smoke/system_test.py +0 -0
  704. {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/smoke/tls_host_test.py +0 -0
  705. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/__init__.py +0 -0
  706. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/_http_error_details.py +0 -0
  707. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/analytics/_helpers.py +0 -0
  708. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/__init__.py +0 -0
  709. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_auth_helpers.py +0 -0
  710. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_destructive_delete_helpers.py +0 -0
  711. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_http_helpers.py +0 -0
  712. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_outbox_helpers.py +0 -0
  713. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_user_deletion_helpers.py +0 -0
  714. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_user_helpers.py +0 -0
  715. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/__init__.py +0 -0
  716. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/_auth_cases.py +0 -0
  717. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/analytics/__init__.py +0 -0
  718. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/analytics/router_test.py +0 -0
  719. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/cache/__init__.py +0 -0
  720. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/cache/router_test.py +0 -0
  721. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/comments/__init__.py +0 -0
  722. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/comments/router_test.py +0 -0
  723. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/moderation/__init__.py +0 -0
  724. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/moderation/_auth_cases.py +0 -0
  725. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/moderation/router_test.py +0 -0
  726. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/outbox/__init__.py +0 -0
  727. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/outbox/router_test.py +0 -0
  728. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/posts/__init__.py +0 -0
  729. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/posts/router_test.py +0 -0
  730. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/users/__init__.py +0 -0
  731. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/users/router_test.py +0 -0
  732. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/attachments/_helpers.py +0 -0
  733. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/attachments/attachment_upload_test.py +0 -0
  734. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/auth/__init__.py +0 -0
  735. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/auth/router_test.py +0 -0
  736. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/__init__.py +0 -0
  737. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/authenticated/__init__.py +0 -0
  738. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +0 -0
  739. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/comment_attachments_integration_test.py +0 -0
  740. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/public/__init__.py +0 -0
  741. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/public/router_test.py +0 -0
  742. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/create_idempotency_integration_test.py +0 -0
  743. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/events/router_test.py +0 -0
  744. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/media/ingest_contract_test.py +0 -0
  745. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/mentions/mentions_integration_test.py +0 -0
  746. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/notifications/__init__.py +0 -0
  747. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/notifications/router_test.py +0 -0
  748. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/__init__.py +0 -0
  749. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/authenticated/post_writes_integration_test.py +0 -0
  750. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/comment_count_aggregation_test.py +0 -0
  751. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/followers_visibility_test.py +0 -0
  752. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/post_attachments_integration_test.py +0 -0
  753. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/post_deletion_integration_test.py +0 -0
  754. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/post_stats_dirty_marking_test.py +0 -0
  755. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/public/__init__.py +0 -0
  756. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/public/router_test.py +0 -0
  757. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/public_read_by_id_unavailable_detail_test.py +0 -0
  758. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/system/__init__.py +0 -0
  759. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/system/router_test.py +0 -0
  760. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/__init__.py +0 -0
  761. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/authenticated/__init__.py +0 -0
  762. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/authenticated/avatar_upload_test.py +0 -0
  763. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/authenticated/banner_upload_test.py +0 -0
  764. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/authenticated/router_test.py +0 -0
  765. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/deletion_audit_integration_test.py +0 -0
  766. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/public/__init__.py +0 -0
  767. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/public/router_test.py +0 -0
  768. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/user_deletion_integration_test.py +0 -0
  769. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/__init__.py +0 -0
  770. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/_media_upload_helpers.py +0 -0
  771. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/account_deletion_test.py +0 -0
  772. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/aggregate_comment_stats_test.py +0 -0
  773. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/aggregate_post_stats_test.py +0 -0
  774. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/aggregate_user_stats_test.py +0 -0
  775. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/analytics_prune_test.py +0 -0
  776. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/analytics_refresh_test.py +0 -0
  777. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/attachment_upload_test.py +0 -0
  778. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/avatar_upload_test.py +0 -0
  779. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/banner_upload_test.py +0 -0
  780. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/conftest.py +0 -0
  781. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/create_idempotency_retention_test.py +0 -0
  782. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/moderation_outbox_test.py +0 -0
  783. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/mute_lift_test.py +0 -0
  784. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/read_notifications_retention_test.py +0 -0
  785. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/stale_staging_sweep_test.py +0 -0
  786. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/user_outbox_test.py +0 -0
  787. {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/utils_test.py +0 -0
  788. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/__init__.py +0 -0
  789. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/api/comments/authenticated/comment_request_schemas_test.py +0 -0
  790. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/api/dedupe_preserving_order_test.py +0 -0
  791. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/api/posts/authenticated/post_update_mapper_test.py +0 -0
  792. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/api/users/profile_update_mapper_test.py +0 -0
  793. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/comments/__init__.py +0 -0
  794. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/events/event_service_test.py +0 -0
  795. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/notifications/inbox_service_test.py +0 -0
  796. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/shared/request_context_test.py +0 -0
  797. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/users/deletion_audit_test.py +0 -0
  798. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/core/bundled_alembic_test.py +0 -0
  799. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/core/migrate_cli_test.py +0 -0
  800. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/core/pagination_test.py +0 -0
  801. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/docs/_api_reference.py +0 -0
  802. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/docs/api_routes_test.py +0 -0
  803. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/domains/__init__.py +0 -0
  804. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/domains/comment/__init__.py +0 -0
  805. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/domains/post/models_test.py +0 -0
  806. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/domains/user/service_test.py +0 -0
  807. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/infrastructure/__init__.py +0 -0
  808. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/infrastructure/media/__init__.py +0 -0
  809. {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/infrastructure/media/ssh_finals_publisher_adapter_test.py +0 -0
@@ -37,7 +37,7 @@ Platform contract: **`docs/platform/client-error-visibility.md`**. Domain import
37
37
 
38
38
  ## Catalog constants
39
39
 
40
- **`GENERIC_BAD_REQUEST_DETAIL`** (400), **`GENERIC_NOT_FOUND_DETAIL`** (404), **`GENERIC_FORBIDDEN_DETAIL`** (403), **`GENERIC_CONFLICT_DETAIL`** (409), ingest 413/415, **`GENERIC_INTERNAL_SERVER_ERROR_DETAIL`** (500).
40
+ **`GENERIC_BAD_REQUEST_DETAIL`** (400), **`GENERIC_NOT_FOUND_DETAIL`** (404), **`GENERIC_FORBIDDEN_DETAIL`** (403), **`GENERIC_CONFLICT_DETAIL`** (409), ingest 413/415, **`GENERIC_SERVICE_UNAVAILABLE_DETAIL`** (503), **`GENERIC_INTERNAL_SERVER_ERROR_DETAIL`** (500).
41
41
 
42
42
  ## Anti-enumeration (public routes)
43
43
 
@@ -31,6 +31,14 @@ Diff by **basename** against the host's **`.cursor/`** and applicable **`docs/`*
31
31
 
32
32
  **Do not overwrite** host-only rules, skills, or product docs.
33
33
 
34
+ ### Transactional email (when the bumped core ships it)
35
+
36
+ Remind the host (via upgrade notes / playbook, not code changes here):
37
+
38
+ 1. Put **`register_email_content`** in the **same** shared host bootstrap helper API and worker already call — worker is load-bearing.
39
+ 1. Register branded renderers **before** **`EMAIL_ENABLED=true`** (core default deletion copy is generic English).
40
+ 1. Do **not** **`enqueue_transactional_email`** for **`account_deletion_scheduled`** unless the host re-implements gated checks — use the core gated task.
41
+
34
42
  ### `docs/deployments/guides/` layout
35
43
 
36
44
  Core walkthroughs live under **`docs/deployments/guides/setup/`** (one-time install) and **`docs/deployments/guides/playbook/`** (ops / incident runbooks) — not a flat **`guides/`** directory.
@@ -86,6 +86,11 @@ jobs:
86
86
  RETENTION_POST_VIEWS_DAYS: ${{ vars.RETENTION_POST_VIEWS_DAYS }}
87
87
  RETENTION_READ_NOTIFICATIONS_DAYS: ${{ vars.RETENTION_READ_NOTIFICATIONS_DAYS }}
88
88
  ANALYTICS_REFRESH_INTERVAL_SECONDS: ${{ vars.ANALYTICS_REFRESH_INTERVAL_SECONDS }}
89
+ EMAIL_ENABLED: ${{ vars.EMAIL_ENABLED }}
90
+ EMAIL_PROVIDER: ${{ vars.EMAIL_PROVIDER }}
91
+ EMAIL_API_KEY: ${{ secrets.EMAIL_API_KEY }}
92
+ EMAIL_FROM_ADDRESS: ${{ vars.EMAIL_FROM_ADDRESS }}
93
+ EMAIL_FROM_NAME: ${{ vars.EMAIL_FROM_NAME }}
89
94
  run: |
90
95
  set -euo pipefail
91
96
 
@@ -18,6 +18,7 @@ logs/
18
18
 
19
19
  # Coverage
20
20
  .coverage
21
+ .coverage.*
21
22
  htmlcov/
22
23
  .hypothesis/
23
24
 
@@ -4,6 +4,36 @@ Notable changes to **core-framework** (import **`core_framework`**). Format foll
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.2.0] - 2026-07-20
8
+
9
+ ### Added
10
+
11
+ - **Transactional email (Resend):** Capability-only email domain (port + facade + Resend adapter), **`[email]`** Settings (`provider` allowlist **`resend`**; **`api_key`** / **`from_address`** required when enabled; **`from_name`** composition; values stripped; CR/LF rejected in envelope fields when enabled), content registry with host **`register_email_content`** (replace-on-duplicate; put all kinds in **one** shared host bootstrap helper called from API **and** worker — worker is load-bearing), generic **`enqueue_transactional_email`** / **`process_transactional_email`**, and gated **`process_account_deletion_scheduled_email`** after a **newly** scheduled **`PUT /users/me/account/deletion`**. Schedule/cancel HTTP stay **204**; mail is best-effort (verified Firebase email only; missing/unverified skips without failing the schedule). Default deletion copy is generic English (no `user_id` in body). Deploy: **`EMAIL_*`** vars + **`EMAIL_API_KEY`** secret. Design: **`docs/design/transactional-email-design.md`**.
12
+
13
+ ### Breaking
14
+
15
+ - **Breaking (hosts — config):** Add **`job_timeout`** under **`[worker]`** (required on **`Settings`**; core **`config.toml`** / template ship **300**). Nested env override **`WORKER__JOB_TIMEOUT`**. `Settings` rejects values above **300** so the outbox visibility lease (**420s**) keeps **120s** of queue/finalization headroom. ARQ **`job_completion_wait`** is **`job_timeout` + 30s** (hooks/Redis after the timed coroutine; ARQ 0.28 applies this wait to **SIGTERM and SIGINT**, so local Ctrl+C waits the same budget). Deploy worker compose should keep **`stop_grace_period`** above that wait plus shutdown teardown (core ships **360s** for the default **300s** timeout).
16
+
17
+ ### Changed
18
+
19
+ - **Domain strong-read renames (hosts calling domain services/repositories):** Stats-aggregation and destructive-purge recount helpers that previously used normal-read names now exist only as **`*_strong`** accessors (replica-safe recount / write-adjacent enumeration). Update call sites:
20
+ - **post:** `retrieve_post_ids_by_author_id` → `retrieve_post_ids_by_author_id_strong`; `retrieve_post_like_count` → `retrieve_post_like_count_strong`; `retrieve_post_view_count_delta` → `retrieve_post_view_count_delta_strong` (matching `select_*_strong` on the repository).
21
+ - **comment:** `retrieve_post_comment_count` → `retrieve_post_comment_count_strong`; `retrieve_comment_like_count` → `retrieve_comment_like_count_strong`; `retrieve_comment_reply_count` → `retrieve_comment_reply_count_strong`.
22
+ - **user:** `retrieve_user_follow_counts` → `retrieve_user_follow_counts_strong`.
23
+ - **moderation:** `retrieve_post_report_count` → `retrieve_post_report_count_strong`; `retrieve_comment_report_count` → `retrieve_comment_report_count_strong`.
24
+
25
+ ### Fixed
26
+
27
+ - **Public profile false 404 on snapshot miss:** `GET /users/{target_user_id}/profile` falls back to a strong full-detail load when the Redis/replica presentation snapshot misses but the authoritative profile still exists.
28
+ - **Worker media decode / shutdown timeouts:** Avatar, banner, and attachment encode/probe run in a **warm spawn worker** (one per API/worker process, serialized) with separate **30s admission** and **30s execution** deadlines, so queue wait does not consume image-processing time. Admission saturation leaves active work untouched: attachment ingest returns retryable **503** with **`Retry-After: 30`**, and media worker jobs raise explicit ARQ retry. Execution timeout terminates and replaces the affected Pillow worker. Worker settings wire ARQ **`job_timeout`** from **`[worker].job_timeout`** and **`job_completion_wait`** as **`job_timeout` + 30s**; deploy worker compose uses **`stop_grace_period: 360s`**.
29
+ - **Cache invalidation failure semantics:** Post-write paths (including admin mutations and media) use best-effort invalidation. Public-profile reads independently strong-read current existence and visibility, so failed invalidation may stale presentation fields but cannot expose a full card after a privacy change or keep a deleted profile visible. Admin cache tooling uses strict invalidation: exact-key requests log and continue on per-item failures; pattern-invalidation worker jobs re-raise so the failure is visible in ARQ/logs, but ordinary exceptions are terminal (no ARQ auto-retry)—operators resubmit the pattern request after Redis recovers.
30
+ - **Outbox timeout headroom:** The visibility lease is **420s**, with `Settings` enforcing **120s** of expected queue/finalization headroom beyond the maximum permitted **300s** global worker job timeout.
31
+ - **Event tokens:** Each issued token embeds a fresh ULID payload so impression dedup on `(post_id, token)` no longer collapses unrelated clients that previously shared empty-payload tokens. Docs describe collision risk as negligible rather than impossible.
32
+ - **Unknown mentions:** Post/comment mention references use `UserIdentity.DEFAULT` for missing users instead of echoing the stored `user_id` as `username`, which could fail response validation and 500 feed reads.
33
+ - **Notification failures after commit:** In-app notification attempts after a successful primary write log and swallow errors so a missed notification cannot turn the write into an HTTP 500 or skip follow-up scheduling. Flow docs for post/comment create and follow state this as best-effort (not on the outbox).
34
+ - **Username case sensitivity:** Availability checks and database uniqueness are **case-sensitive** (exact string match in Postgres and the taken-usernames cache). Docs that previously described case-insensitive checks were corrected.
35
+ - **Strong reads for write-adjacent counts:** Stats aggregation recounts and destructive user purge post-id enumeration use strong reads so replica lag cannot permanently stale counters or leave orphan comments. Flow docs link to platform/design for that mechanics; they no longer embed strong-read implementation detail.
36
+
7
37
  ## [3.1.0] - 2026-07-16
8
38
 
9
39
  ### Fixed
@@ -572,4 +602,5 @@ First **SemVer-stable** release per **`docs/library/package-api.md`**.
572
602
  [2.3.3]: https://github.com/NepNepFFXIV/core-framework/compare/v2.3.2...v2.3.3
573
603
  [3.0.0]: https://github.com/NepNepFFXIV/core-framework/compare/v2.3.3...v3.0.0
574
604
  [3.1.0]: https://github.com/NepNepFFXIV/core-framework/compare/v3.0.0...v3.1.0
575
- [unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v3.1.0...HEAD
605
+ [3.2.0]: https://github.com/NepNepFFXIV/core-framework/compare/v3.1.0...v3.2.0
606
+ [unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v3.2.0...HEAD
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: core-framework
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: Core framework package (import as core_framework)
5
5
  Project-URL: Homepage, https://github.com/NepNepFFXIV/core-framework
6
6
  Project-URL: Repository, https://github.com/NepNepFFXIV/core-framework
@@ -34,6 +34,7 @@ Requires-Dist: pillow-heif>=1.4.0
34
34
  Requires-Dist: pillow>=12.3.0
35
35
  Requires-Dist: pyinstrument>=5.1.2
36
36
  Requires-Dist: python-ulid>=3.1.0
37
+ Requires-Dist: resend[async]>=2.34.0
37
38
  Requires-Dist: structlog>=26.1.0
38
39
  Requires-Dist: tenacity>=9.1.4
39
40
  Provides-Extra: testing
@@ -114,6 +114,7 @@ max_connections = 4
114
114
  [worker]
115
115
  max_jobs = 10
116
116
  max_tries = 2
117
+ job_timeout = 300
117
118
 
118
119
  [retention]
119
120
  post_views_retention_days = 90
@@ -137,3 +138,10 @@ host = "127.0.0.1"
137
138
  port = 6379
138
139
  cache_db = 0
139
140
  queue_db = 1
141
+
142
+ [email]
143
+ enabled = false
144
+ provider = "resend"
145
+ api_key = ""
146
+ from_address = ""
147
+ from_name = ""
@@ -98,6 +98,7 @@ max_connections = 4
98
98
  [worker]
99
99
  max_jobs = 10
100
100
  max_tries = 2
101
+ job_timeout = 300
101
102
 
102
103
  [retention]
103
104
  post_views_retention_days = $RETENTION_POST_VIEWS_DAYS
@@ -121,3 +122,10 @@ host = "$REDIS_HOST"
121
122
  port = $REDIS_PORT
122
123
  cache_db = 0
123
124
  queue_db = 1
125
+
126
+ [email]
127
+ enabled = $EMAIL_ENABLED
128
+ provider = "$EMAIL_PROVIDER"
129
+ api_key = "$EMAIL_API_KEY"
130
+ from_address = "$EMAIL_FROM_ADDRESS"
131
+ from_name = "$EMAIL_FROM_NAME"
@@ -1,6 +1,7 @@
1
1
  import core_framework.domains.moderation.dependencies as moderation_deps
2
2
  import core_framework.domains.user.dependencies as user_deps
3
3
  from core_framework.application.analytics.registry import configure_activity_sources
4
+ from core_framework.application.email.registry import configure_email_content_registry
4
5
  from core_framework.application.events.event_service import configure_event_handlers
5
6
  from core_framework.application.events.event_token import configure_event_token
6
7
  from core_framework.application.events.utm_capture_kinds import configure_utm_capture_kinds
@@ -13,6 +14,7 @@ from core_framework.core.runtime import CoreRuntime
13
14
  from core_framework.domains.analytics.dependencies import configure_analytics_dependencies
14
15
  from core_framework.domains.auth.dependencies import configure_auth_dependencies
15
16
  from core_framework.domains.comment.dependencies import configure_comment_dependencies
17
+ from core_framework.domains.email.dependencies import configure_email_dependencies
16
18
  from core_framework.domains.media.dependencies import configure_media_dependencies
17
19
  from core_framework.domains.moderation.dependencies import configure_moderation_dependencies
18
20
  from core_framework.domains.notification.dependencies import configure_notification_dependencies
@@ -35,11 +37,13 @@ def configure_application_dependencies(*, runtime: CoreRuntime) -> None:
35
37
  """Wire application-level dependencies from the shared runtime."""
36
38
  configure_event_handlers()
37
39
  configure_utm_capture_kinds()
40
+ configure_email_content_registry()
38
41
  configure_activity_sources()
39
42
  configure_worker_jobs(runtime=runtime)
40
43
  configure_cashews_backend(redis_cache_url=runtime.settings.redis.redis_cache_url)
41
44
  configure_auth_dependencies()
42
45
  configure_comment_dependencies(runtime)
46
+ configure_email_dependencies(runtime)
43
47
  configure_media_dependencies(runtime)
44
48
  configure_moderation_dependencies(runtime)
45
49
  configure_notification_dependencies(runtime)
@@ -9,10 +9,14 @@ from core_framework.domains.comment import CommentStatsImpact, CommentSubjectTyp
9
9
  async def aggregate_comment_stats(*, comment_id: str) -> None:
10
10
  """Recompute and persist aggregate stats for a comment."""
11
11
  async with TaskGroup() as tg:
12
- like_task = tg.create_task(comment_deps.comment_service.retrieve_comment_like_count(comment_id=comment_id))
13
- reply_task = tg.create_task(comment_deps.comment_service.retrieve_comment_reply_count(comment_id=comment_id))
12
+ like_task = tg.create_task(
13
+ comment_deps.comment_service.retrieve_comment_like_count_strong(comment_id=comment_id)
14
+ )
15
+ reply_task = tg.create_task(
16
+ comment_deps.comment_service.retrieve_comment_reply_count_strong(comment_id=comment_id)
17
+ )
14
18
  report_task = tg.create_task(
15
- moderation_deps.moderation_service.retrieve_comment_report_count(comment_id=comment_id)
19
+ moderation_deps.moderation_service.retrieve_comment_report_count_strong(comment_id=comment_id)
16
20
  )
17
21
 
18
22
  await comment_deps.comment_service.update_comment_stats(
@@ -1,7 +1,7 @@
1
1
  from asyncio import TaskGroup
2
2
  from collections import defaultdict
3
3
  from datetime import datetime
4
- from typing import Any, Final
4
+ from typing import Any
5
5
 
6
6
  import core_framework.domains.comment.dependencies as comment_deps
7
7
  import core_framework.domains.moderation.dependencies as moderation_deps
@@ -20,7 +20,6 @@ from core_framework.domains.notification import NotificationMuteSubjectType
20
20
  from core_framework.domains.user import UserIdentity
21
21
 
22
22
  _MAX_AUTHOR_COMMENT_VISIBILITY_FETCH_ROUNDS = 10
23
- _UNKNOWN_MENTION_DISPLAY_NAME: Final[str] = "unknown"
24
23
 
25
24
 
26
25
  def _mention_references(
@@ -28,26 +27,15 @@ def _mention_references(
28
27
  mentioned_user_ids: list[str],
29
28
  user_identity_mapping: defaultdict[str, UserIdentity],
30
29
  ) -> list[dict[str, Any]]:
31
- mentions: list[dict[str, Any]] = []
32
- for user_id in mentioned_user_ids:
33
- identity = user_identity_mapping[user_id]
34
- if identity.user_id == user_id:
35
- mentions.append(
36
- {
37
- "user_id": user_id,
38
- "username": identity.username,
39
- "display_name": identity.display_name,
40
- }
41
- )
42
- else:
43
- mentions.append(
44
- {
45
- "user_id": user_id,
46
- "username": user_id,
47
- "display_name": _UNKNOWN_MENTION_DISPLAY_NAME,
48
- }
49
- )
50
- return mentions
30
+ """Build mention ``UserReference`` dicts; unknown ids use ``UserIdentity.DEFAULT``."""
31
+ return [
32
+ {
33
+ "user_id": user_id,
34
+ "username": user_identity_mapping[user_id].username,
35
+ "display_name": user_identity_mapping[user_id].display_name,
36
+ }
37
+ for user_id in mentioned_user_ids
38
+ ]
51
39
 
52
40
 
53
41
  def _mentioned_user_ids_for_comments(
@@ -0,0 +1,29 @@
1
+ """Transactional email content registry and generic enqueue (host surface).
2
+
3
+ Hosts put every ``register_email_content`` call in one shared bootstrap helper
4
+ (alongside UTM / activity sources) and invoke that helper from both API and
5
+ worker after ``configure_application_dependencies``. Worker registration is
6
+ load-bearing; API-only register still enqueues but the worker keeps core
7
+ defaults or hits ``missing_renderer``. Register branded renderers before enabling
8
+ email in production (core default deletion copy is generic English).
9
+
10
+ Enqueue host-owned kinds with ``enqueue_transactional_email``. Do not use it for
11
+ core gated kinds such as ``account_deletion_scheduled`` unless the host
12
+ re-implements those gates (prefer the gated core task).
13
+
14
+ Render+send runs only on the worker (via core tasks); it is not part of this
15
+ public export — do not call it from the API process. Registry reset
16
+ (``configure_email_content_registry``) is bootstrap-internal only.
17
+ """
18
+
19
+ from core_framework.application.email.enqueue import enqueue_transactional_email
20
+ from core_framework.application.email.registry import (
21
+ ACCOUNT_DELETION_SCHEDULED_KIND,
22
+ register_email_content,
23
+ )
24
+
25
+ __all__ = [
26
+ "ACCOUNT_DELETION_SCHEDULED_KIND",
27
+ "enqueue_transactional_email",
28
+ "register_email_content",
29
+ ]
@@ -0,0 +1,77 @@
1
+ from datetime import UTC
2
+
3
+ from structlog import get_logger
4
+
5
+ import core_framework.domains.auth.dependencies as auth_deps
6
+ import core_framework.domains.user.dependencies as user_deps
7
+ from core_framework.application.email.registry import ACCOUNT_DELETION_SCHEDULED_KIND
8
+ from core_framework.application.email.send import render_and_send_transactional_email
9
+
10
+ logger = get_logger(__name__)
11
+
12
+
13
+ async def process_account_deletion_scheduled_email(*, user_id: str) -> None:
14
+ """Send ``account_deletion_scheduled`` mail when the schedule and verified email allow."""
15
+ scheduled_for = await user_deps.user_service.retrieve_user_deletion_scheduled_for_strong(user_id=user_id)
16
+ if scheduled_for is None:
17
+ logger.info(
18
+ "Skipped account deletion scheduled email",
19
+ reason="canceled",
20
+ user_id=user_id,
21
+ kind=ACCOUNT_DELETION_SCHEDULED_KIND,
22
+ )
23
+ return
24
+
25
+ try:
26
+ firebase_user = await auth_deps.auth_service.get_user(user_id=user_id)
27
+ except Exception:
28
+ logger.exception(
29
+ "Account deletion scheduled email auth lookup failed",
30
+ reason="auth_lookup_failure",
31
+ user_id=user_id,
32
+ kind=ACCOUNT_DELETION_SCHEDULED_KIND,
33
+ )
34
+ return
35
+
36
+ if firebase_user is None:
37
+ logger.info(
38
+ "Skipped account deletion scheduled email",
39
+ reason="auth_user_missing",
40
+ user_id=user_id,
41
+ kind=ACCOUNT_DELETION_SCHEDULED_KIND,
42
+ )
43
+ return
44
+
45
+ email = (firebase_user.email or "").strip()
46
+ if not email:
47
+ logger.info(
48
+ "Skipped account deletion scheduled email",
49
+ reason="missing_email",
50
+ user_id=user_id,
51
+ kind=ACCOUNT_DELETION_SCHEDULED_KIND,
52
+ )
53
+ return
54
+
55
+ if not firebase_user.email_verified:
56
+ logger.info(
57
+ "Skipped account deletion scheduled email",
58
+ reason="unverified_email",
59
+ user_id=user_id,
60
+ kind=ACCOUNT_DELETION_SCHEDULED_KIND,
61
+ )
62
+ return
63
+
64
+ context: dict[str, object] = {
65
+ "user_id": user_id,
66
+ "deletion_date": scheduled_for.astimezone(UTC).date().isoformat(),
67
+ "grace_period_days": user_deps.user_service.ACCOUNT_DELETION_GRACE_DAYS,
68
+ }
69
+ identity = await user_deps.user_service.retrieve_user_identity(user_id=user_id)
70
+ if identity.user_id == user_id:
71
+ context["username"] = identity.username
72
+
73
+ await render_and_send_transactional_email(
74
+ to=email,
75
+ kind=ACCOUNT_DELETION_SCHEDULED_KIND,
76
+ context=context,
77
+ )
@@ -0,0 +1,36 @@
1
+ from collections.abc import Mapping
2
+ from typing import Any
3
+
4
+ from structlog import get_logger
5
+
6
+ import core_framework.application.shared.worker_jobs as worker_jobs
7
+ from core_framework.application.shared.enums import WorkerJobName
8
+
9
+ logger = get_logger(__name__)
10
+
11
+
12
+ async def enqueue_transactional_email(
13
+ *,
14
+ to: str,
15
+ kind: str,
16
+ context: Mapping[str, Any],
17
+ _job_id: str | None = None,
18
+ ) -> None:
19
+ """Best-effort enqueue of ``process_transactional_email``."""
20
+ trimmed_kind = kind.strip()
21
+ if not trimmed_kind:
22
+ raise ValueError("email content kind must not be empty or whitespace-only")
23
+ job_kwargs: dict[str, Any] = {
24
+ "to": to,
25
+ "kind": trimmed_kind,
26
+ "context": dict(context),
27
+ }
28
+ if _job_id is not None:
29
+ job_kwargs["_job_id"] = _job_id
30
+ try:
31
+ await worker_jobs.enqueue_job(
32
+ job_name=WorkerJobName.PROCESS_TRANSACTIONAL_EMAIL,
33
+ **job_kwargs,
34
+ )
35
+ except Exception:
36
+ logger.exception("Failed to enqueue transactional email", kind=trimmed_kind)
@@ -0,0 +1,64 @@
1
+ from collections.abc import Callable, Mapping
2
+ from html import escape
3
+ from typing import Any, Final
4
+
5
+ ACCOUNT_DELETION_SCHEDULED_KIND: Final[str] = "account_deletion_scheduled"
6
+
7
+ # Sync pure function: context → (subject, html_body, plain_text_body). No I/O.
8
+ type EmailRenderer = Callable[[Mapping[str, Any]], tuple[str, str, str]]
9
+
10
+ _renderers: dict[str, EmailRenderer] = {}
11
+
12
+
13
+ def configure_email_content_registry() -> None:
14
+ """Reset the email content registry and register core default renderers."""
15
+ global _renderers
16
+ _renderers = {}
17
+ register_email_content(
18
+ ACCOUNT_DELETION_SCHEDULED_KIND,
19
+ render_account_deletion_scheduled,
20
+ )
21
+
22
+
23
+ def register_email_content(kind: str, renderer: EmailRenderer) -> None:
24
+ """Register or replace a transactional email renderer for ``kind``."""
25
+ trimmed = kind.strip()
26
+ if not trimmed:
27
+ raise ValueError("email content kind must not be empty or whitespace-only")
28
+ _renderers[trimmed] = renderer
29
+
30
+
31
+ def get_email_renderer(kind: str) -> EmailRenderer | None:
32
+ """Return the registered renderer for ``kind``, or ``None`` if missing."""
33
+ return _renderers.get(kind.strip())
34
+
35
+
36
+ def render_account_deletion_scheduled(context: Mapping[str, Any]) -> tuple[str, str, str]:
37
+ """Default English copy for ``account_deletion_scheduled`` (HTML-escapes interpolations)."""
38
+ _ = context["user_id"] # required context key; not shown in copy
39
+ deletion_date = context["deletion_date"]
40
+ grace_period_days = context["grace_period_days"]
41
+ username = context.get("username")
42
+
43
+ deletion_date_str = str(deletion_date)
44
+ grace_str = str(grace_period_days)
45
+ greeting_plain = f"Hi {username}," if username is not None else "Hi,"
46
+ greeting_html = f"Hi {escape(str(username))}," if username is not None else "Hi,"
47
+
48
+ subject = "Your account is scheduled for deletion"
49
+ plain_text_body = (
50
+ f"{greeting_plain}\n\n"
51
+ f"Your account is scheduled for deletion on {deletion_date_str}. "
52
+ f"You have a {grace_str}-day grace period.\n\n"
53
+ "If you did not request this, cancel the deletion from your account settings "
54
+ "before that date.\n"
55
+ )
56
+ html_body = (
57
+ f"<p>{greeting_html}</p>"
58
+ f"<p>Your account is scheduled for deletion on "
59
+ f"<strong>{escape(deletion_date_str)}</strong>. "
60
+ f"You have a {escape(grace_str)}-day grace period.</p>"
61
+ "<p>If you did not request this, cancel the deletion from your account settings "
62
+ "before that date.</p>"
63
+ )
64
+ return subject, html_body, plain_text_body
@@ -0,0 +1,52 @@
1
+ from collections.abc import Mapping
2
+ from typing import Any
3
+
4
+ from structlog import get_logger
5
+
6
+ import core_framework.domains.email.dependencies as email_deps
7
+ from core_framework.application.email.registry import get_email_renderer
8
+
9
+ logger = get_logger(__name__)
10
+
11
+
12
+ async def render_and_send_transactional_email(
13
+ *,
14
+ to: str,
15
+ kind: str,
16
+ context: Mapping[str, Any],
17
+ ) -> None:
18
+ """Render a registered kind and send via the email facade (worker-facing)."""
19
+ kind = kind.strip()
20
+ email_service = email_deps.email_service
21
+ if not email_service.enabled:
22
+ await email_service.send(
23
+ to="",
24
+ subject="",
25
+ html_body="",
26
+ plain_text_body="",
27
+ kind=kind,
28
+ )
29
+ return
30
+
31
+ renderer = get_email_renderer(kind)
32
+ if renderer is None:
33
+ logger.error(
34
+ "Transactional email renderer missing",
35
+ reason="missing_renderer",
36
+ kind=kind,
37
+ )
38
+ raise RuntimeError(f"missing email renderer for kind {kind!r}")
39
+
40
+ try:
41
+ subject, html_body, plain_text_body = renderer(context)
42
+ except Exception:
43
+ logger.exception("Transactional email render failed", kind=kind)
44
+ raise
45
+
46
+ await email_service.send(
47
+ to=to,
48
+ subject=subject,
49
+ html_body=html_body,
50
+ plain_text_body=plain_text_body,
51
+ kind=kind,
52
+ )
@@ -1,5 +1,6 @@
1
1
  from itsdangerous import BadSignature, SignatureExpired, URLSafeTimedSerializer
2
2
  from structlog import get_logger
3
+ from ulid import ULID
3
4
 
4
5
  logger = get_logger(__name__)
5
6
 
@@ -24,16 +25,12 @@ def _get_serializer() -> URLSafeTimedSerializer:
24
25
 
25
26
 
26
27
  def generate_token() -> str:
27
- """Return a signed token clients include when posting analytics events."""
28
- return _get_serializer().dumps("")
28
+ """Return a signed token with a unique ULID payload for analytics event posts."""
29
+ return _get_serializer().dumps(str(ULID()))
29
30
 
30
31
 
31
32
  def verify_token(*, token: str) -> bool:
32
- """Return whether the event token is valid (signed and not expired).
33
-
34
- The token is type-agnostic: one global max-age applies to every event type,
35
- so hosts can introduce event types without a core token-policy change.
36
- """
33
+ """Return whether the event token is valid (signed and not expired)."""
37
34
  try:
38
35
  _get_serializer().loads(token, max_age=EVENT_TOKEN_MAX_AGE_SECONDS)
39
36
  return True
@@ -1,5 +1,6 @@
1
1
  from collections.abc import AsyncIterator, Awaitable
2
2
  from dataclasses import dataclass
3
+ from math import ceil
3
4
  from typing import Final, Protocol
4
5
 
5
6
  from structlog import get_logger
@@ -14,7 +15,9 @@ from core_framework.application.shared.exceptions import (
14
15
  BadRequestException,
15
16
  IngestBodyTooLargeException,
16
17
  IngestUnsupportedMediaTypeException,
18
+ ServiceUnavailableException,
17
19
  )
20
+ from core_framework.core.process import ProcessAdmissionTimeoutError
18
21
  from core_framework.domains.media import (
19
22
  ATTACHMENT_STAGING_TTL_DAYS,
20
23
  AttachmentIngestRegistration,
@@ -61,12 +64,17 @@ async def accept_attachment_ingest_upload(
61
64
  raise IngestBodyTooLargeException()
62
65
  if staging_bytes is None:
63
66
  raise BadRequestException()
64
- (
65
- probe_outcome,
66
- display_dimensions,
67
- ) = await media_deps.attachment_service.try_probe_attachment_display_dimensions_from_staging(
68
- staging_bytes=staging_bytes,
69
- )
67
+ try:
68
+ (
69
+ probe_outcome,
70
+ display_dimensions,
71
+ ) = await media_deps.attachment_service.try_probe_attachment_display_dimensions_from_staging(
72
+ staging_bytes=staging_bytes,
73
+ )
74
+ except ProcessAdmissionTimeoutError as exc:
75
+ raise ServiceUnavailableException(
76
+ retry_after_seconds=max(1, ceil(exc.timeout_seconds)),
77
+ ) from exc
70
78
  if probe_outcome == AttachmentStagingImageProbeOutcome.PROCESSING_FAILED or display_dimensions is None:
71
79
  raise BadRequestException()
72
80
  display_width, display_height = display_dimensions
@@ -13,7 +13,7 @@ from core_framework.application.shared.exceptions import (
13
13
  IngestUnsupportedMediaTypeException,
14
14
  )
15
15
  from core_framework.core import CacheKey
16
- from core_framework.core.cache import invalidate_cache
16
+ from core_framework.core.cache import invalidate_cache_best_effort
17
17
  from core_framework.domains.media import (
18
18
  AvatarIngestRegistration,
19
19
  IngestBodyOutcome,
@@ -142,8 +142,8 @@ async def process_media_asset_variants(*, owner_user_id: str, asset_id: str, ing
142
142
  actor_id=owner_user_id,
143
143
  )
144
144
  if apply_result.applied:
145
- await invalidate_cache(CacheKey.USER_PUBLIC_PROFILE, user_id=owner_user_id)
146
- await invalidate_cache(CacheKey.USER_DETAIL, user_id=owner_user_id)
145
+ await invalidate_cache_best_effort(CacheKey.USER_PUBLIC_PROFILE, user_id=owner_user_id)
146
+ await invalidate_cache_best_effort(CacheKey.USER_DETAIL, user_id=owner_user_id)
147
147
  if apply_result.old_avatar_id is not None:
148
148
  await enqueue_delete_superseded_avatar_finals(asset_id=apply_result.old_avatar_id)
149
149
  await media_deps.avatar_service.delete_avatar_staging_blob(staging_filename=registry_entry.staging_filename)
@@ -13,7 +13,7 @@ from core_framework.application.shared.exceptions import (
13
13
  IngestUnsupportedMediaTypeException,
14
14
  )
15
15
  from core_framework.core import CacheKey
16
- from core_framework.core.cache import invalidate_cache
16
+ from core_framework.core.cache import invalidate_cache_best_effort
17
17
  from core_framework.domains.media import (
18
18
  BannerIngestRegistration,
19
19
  IngestBodyOutcome,
@@ -138,8 +138,8 @@ async def process_banner_asset_variants(*, owner_user_id: str, asset_id: str, in
138
138
  actor_id=owner_user_id,
139
139
  )
140
140
  if apply_result.applied:
141
- await invalidate_cache(CacheKey.USER_PUBLIC_PROFILE, user_id=owner_user_id)
142
- await invalidate_cache(CacheKey.USER_DETAIL, user_id=owner_user_id)
141
+ await invalidate_cache_best_effort(CacheKey.USER_PUBLIC_PROFILE, user_id=owner_user_id)
142
+ await invalidate_cache_best_effort(CacheKey.USER_DETAIL, user_id=owner_user_id)
143
143
  if apply_result.old_banner_id is not None:
144
144
  await enqueue_delete_superseded_banner_finals(asset_id=apply_result.old_banner_id)
145
145
  await media_deps.banner_service.delete_banner_staging_blob(staging_filename=registry_entry.staging_filename)