core-framework 2.1.0__tar.gz → 2.3.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 (670) hide show
  1. core_framework-2.1.0/.cursor/rules/alembic-revision-ids.mdc → core_framework-2.3.0/.cursor/rules/alembic-migrations.mdc +26 -5
  2. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/api-layer.mdc +1 -1
  3. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/application-layer.mdc +1 -1
  4. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/constitution.mdc +7 -1
  5. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/domain-caller-context.mdc +1 -1
  6. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/domain-imports.mdc +2 -2
  7. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/domain-repository-exceptions.mdc +5 -2
  8. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/error-boundary.mdc +4 -3
  9. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/layer-boundaries.mdc +1 -1
  10. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/strong-reads.mdc +4 -4
  11. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/skills/add-domain/SKILL.md +1 -1
  12. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/skills/code-review/SKILL.md +1 -1
  13. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/skills/implement-feature/SKILL.md +1 -1
  14. {core_framework-2.1.0 → core_framework-2.3.0}/.github/workflows/_deploy.yml +3 -0
  15. {core_framework-2.1.0 → core_framework-2.3.0}/.github/workflows/test.yaml +5 -10
  16. {core_framework-2.1.0 → core_framework-2.3.0}/CHANGELOG.md +52 -1
  17. {core_framework-2.1.0 → core_framework-2.3.0}/PKG-INFO +1 -1
  18. core_framework-2.3.0/alembic/analytics/alembic/env.py +74 -0
  19. core_framework-2.3.0/alembic/analytics/alembic/versions/5b9ac98bb150_analytics_daily_fact.py +33 -0
  20. core_framework-2.3.0/alembic/analytics/alembic/versions/7f2a9c1d4e8b_analytics_daily_fact_day_index.py +27 -0
  21. core_framework-2.3.0/alembic/moderation/alembic/versions/moderation_add_event_outbox.py +52 -0
  22. core_framework-2.3.0/alembic/moderation/alembic/versions/moderation_add_event_outbox_dead_indexes.py +34 -0
  23. core_framework-2.3.0/alembic/moderation/alembic/versions/moderation_add_event_outbox_pending_created_at_index.py +28 -0
  24. core_framework-2.3.0/alembic/notification/alembic.ini +52 -0
  25. core_framework-2.3.0/alembic/post/alembic/versions/post_post_views_retention_high_water_mark.py +64 -0
  26. core_framework-2.3.0/alembic/user/alembic/README +1 -0
  27. core_framework-2.3.0/alembic/user/alembic/script.py.mako +28 -0
  28. core_framework-2.3.0/alembic/user/alembic/versions/user_add_event_outbox.py +52 -0
  29. core_framework-2.3.0/alembic/user/alembic/versions/user_add_event_outbox_dead_indexes.py +34 -0
  30. core_framework-2.3.0/alembic/user/alembic/versions/user_add_event_outbox_pending_created_at_index.py +28 -0
  31. {core_framework-2.1.0 → core_framework-2.3.0}/config.toml +19 -1
  32. {core_framework-2.1.0 → core_framework-2.3.0}/config.toml.template +20 -2
  33. core_framework-2.3.0/core_framework/api/admin/analytics/router.py +44 -0
  34. core_framework-2.3.0/core_framework/api/admin/analytics/schemas.py +59 -0
  35. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/cache/router.py +2 -8
  36. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/comments/router.py +3 -13
  37. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/moderation/router.py +2 -10
  38. core_framework-2.3.0/core_framework/api/admin/outbox/router.py +71 -0
  39. core_framework-2.3.0/core_framework/api/admin/outbox/schemas.py +56 -0
  40. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/posts/router.py +3 -12
  41. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/router.py +4 -0
  42. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/users/router.py +2 -10
  43. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/users/schemas.py +1 -1
  44. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/comments/authenticated/router.py +5 -25
  45. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/comments/authenticated/schemas.py +1 -4
  46. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/events/router.py +12 -11
  47. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/events/schemas.py +4 -3
  48. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/notifications/authenticated/router.py +2 -8
  49. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/posts/authenticated/router.py +1 -5
  50. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/posts/authenticated/schemas.py +1 -4
  51. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/authenticated/router.py +10 -41
  52. core_framework-2.3.0/core_framework/application/analytics/bucket_service.py +43 -0
  53. core_framework-2.3.0/core_framework/application/analytics/prune_service.py +6 -0
  54. core_framework-2.3.0/core_framework/application/analytics/read_service.py +49 -0
  55. core_framework-2.3.0/core_framework/application/analytics/refresh_service.py +52 -0
  56. core_framework-2.3.0/core_framework/application/analytics/registry.py +36 -0
  57. core_framework-2.3.0/core_framework/application/analytics/sources/comment_created.py +26 -0
  58. core_framework-2.3.0/core_framework/application/analytics/sources/post_created.py +26 -0
  59. core_framework-2.3.0/core_framework/application/analytics/sources/post_viewed.py +26 -0
  60. core_framework-2.3.0/core_framework/application/analytics/sources/user_registered.py +26 -0
  61. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/bootstrap.py +18 -0
  62. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/comments/admin_service.py +2 -10
  63. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/comments/authenticated_service.py +5 -15
  64. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/comments/public_service.py +2 -11
  65. core_framework-2.3.0/core_framework/application/events/README.md +35 -0
  66. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/events/event_service.py +29 -21
  67. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/events/event_token.py +7 -12
  68. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/events/models.py +1 -3
  69. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/media/README.md +3 -1
  70. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/media/attachment_service.py +12 -23
  71. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/media/avatar_service.py +8 -13
  72. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/media/banner_service.py +8 -13
  73. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/media/ingest_guards.py +1 -5
  74. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/moderation/appeal_service.py +43 -39
  75. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/moderation/moderator_service.py +1 -6
  76. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/moderation/user_service.py +42 -64
  77. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/notifications/inbox_service.py +2 -11
  78. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/notifications/mute_service.py +1 -5
  79. core_framework-2.3.0/core_framework/application/outbox/block_mirrors.py +54 -0
  80. core_framework-2.3.0/core_framework/application/outbox/events.py +109 -0
  81. core_framework-2.3.0/core_framework/application/outbox/exceptions.py +2 -0
  82. core_framework-2.3.0/core_framework/application/outbox/follow_mirrors.py +20 -0
  83. core_framework-2.3.0/core_framework/application/outbox/handlers.py +115 -0
  84. core_framework-2.3.0/core_framework/application/outbox/ops_service.py +77 -0
  85. core_framework-2.3.0/core_framework/application/outbox/polling_service.py +64 -0
  86. core_framework-2.3.0/core_framework/application/outbox/processing_service.py +69 -0
  87. core_framework-2.3.0/core_framework/application/outbox/restriction_mirrors.py +46 -0
  88. core_framework-2.3.0/core_framework/application/posts/aggregation_service.py +54 -0
  89. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/posts/authenticated_service.py +2 -13
  90. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/posts/public_service.py +7 -6
  91. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/shared/enums.py +2 -0
  92. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/shared/exceptions.py +6 -0
  93. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/users/admin_service.py +7 -32
  94. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/users/authenticated_service.py +53 -52
  95. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/users/public_service.py +2 -2
  96. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/users/user_deletion_service.py +6 -2
  97. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/bundled_alembic.py +1 -0
  98. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/database.py +1 -6
  99. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/exception_handlers/application.py +17 -28
  100. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/exception_handlers/common.py +2 -8
  101. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/exception_handlers/setup.py +8 -8
  102. core_framework-2.3.0/core_framework/core/exceptions.py +2 -0
  103. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/logging.py +1 -5
  104. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/middleware.py +1 -6
  105. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/pagination.py +4 -2
  106. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/runtime.py +6 -0
  107. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/settings.py +81 -9
  108. core_framework-2.3.0/core_framework/domains/analytics/README.md +26 -0
  109. core_framework-2.3.0/core_framework/domains/analytics/__init__.py +13 -0
  110. core_framework-2.3.0/core_framework/domains/analytics/constants.py +3 -0
  111. core_framework-2.3.0/core_framework/domains/analytics/dependencies.py +59 -0
  112. core_framework-2.3.0/core_framework/domains/analytics/exceptions.py +5 -0
  113. core_framework-2.3.0/core_framework/domains/analytics/models.py +17 -0
  114. core_framework-2.3.0/core_framework/domains/analytics/ports/activity_source.py +37 -0
  115. core_framework-2.3.0/core_framework/domains/analytics/repository.py +176 -0
  116. core_framework-2.3.0/core_framework/domains/analytics/service.py +204 -0
  117. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/auth/service.py +4 -4
  118. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/README.md +3 -2
  119. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/repository.py +61 -83
  120. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/service.py +41 -73
  121. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/components/finals_publisher.py +1 -7
  122. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/ports/finals_publisher.py +1 -7
  123. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/service.py +3 -13
  124. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/service.py +4 -20
  125. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/service.py +3 -13
  126. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/shared/repository.py +4 -24
  127. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/moderation/dependencies.py +28 -2
  128. core_framework-2.3.0/core_framework/domains/moderation/outbox_repository.py +179 -0
  129. core_framework-2.3.0/core_framework/domains/moderation/outbox_service.py +52 -0
  130. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/moderation/repository.py +93 -26
  131. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/moderation/service.py +86 -24
  132. core_framework-2.3.0/core_framework/domains/outbox/__init__.py +17 -0
  133. core_framework-2.3.0/core_framework/domains/outbox/dependencies.py +61 -0
  134. core_framework-2.3.0/core_framework/domains/outbox/models.py +79 -0
  135. core_framework-2.3.0/core_framework/domains/outbox/ports/admin.py +35 -0
  136. core_framework-2.3.0/core_framework/domains/outbox/ports/consumer.py +78 -0
  137. core_framework-2.3.0/core_framework/domains/outbox/ports/producer.py +42 -0
  138. core_framework-2.3.0/core_framework/domains/outbox/service.py +66 -0
  139. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/README.md +6 -5
  140. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/models.py +12 -0
  141. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/repository.py +216 -124
  142. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/service.py +122 -48
  143. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/dependencies.py +28 -2
  144. core_framework-2.3.0/core_framework/domains/user/outbox_repository.py +178 -0
  145. core_framework-2.3.0/core_framework/domains/user/outbox_service.py +52 -0
  146. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/outcomes.py +1 -0
  147. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/repository.py +216 -59
  148. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/service.py +127 -53
  149. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/utils.py +15 -0
  150. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/ssh_attachment_finals_publisher_adapter.py +3 -21
  151. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/ssh_banner_finals_publisher_adapter.py +2 -14
  152. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/ssh_finals_publisher_adapter.py +2 -14
  153. core_framework-2.3.0/core_framework/infrastructure/outbox/outbox_consumer_adapter.py +150 -0
  154. core_framework-2.3.0/core_framework/infrastructure/outbox/outbox_producer_adapter.py +71 -0
  155. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/arq.py +22 -20
  156. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/auth.py +62 -48
  157. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/containers.py +1 -5
  158. core_framework-2.3.0/core_framework/testing/firebase_identity_toolkit.py +20 -0
  159. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/media.py +2 -14
  160. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/main.py +19 -4
  161. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/__init__.py +37 -0
  162. core_framework-2.3.0/core_framework/worker/schedules/analytics_refresh_cron.py +18 -0
  163. core_framework-2.3.0/core_framework/worker/schedules/schedule_poll_outbox.py +42 -0
  164. core_framework-2.3.0/core_framework/worker/schedules/schedule_prune_analytics_daily_facts.py +16 -0
  165. core_framework-2.3.0/core_framework/worker/schedules/schedule_prune_post_views.py +17 -0
  166. core_framework-2.3.0/core_framework/worker/schedules/schedule_refresh_analytics.py +7 -0
  167. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/schedule_sweep_stale_avatar_staging.py +2 -2
  168. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/schedule_sweep_stale_banner_staging.py +2 -2
  169. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/__init__.py +22 -0
  170. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_attachment_asset_variants.py +1 -6
  171. core_framework-2.3.0/core_framework/worker/tasks/process_outbox_row.py +7 -0
  172. core_framework-2.3.0/core_framework/worker/tasks/process_refresh_analytics_bucket.py +19 -0
  173. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/deploy-playbook.md +6 -0
  174. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/e2e-smoke-tests-design.md +1 -1
  175. core_framework-2.3.0/docs/domains/README.md +18 -0
  176. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/auth/access_control.md +1 -0
  177. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/cache/admin_cache_invalidation.md +9 -9
  178. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/comments/admin_comments.md +1 -1
  179. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/deletion/comments.md +2 -1
  180. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/deletion/overview.md +12 -3
  181. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/deletion/posts.md +36 -23
  182. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/deletion/users.md +20 -19
  183. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/events/events.md +21 -7
  184. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/media/avatar-upload-design.md +1 -1
  185. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/media/banner-upload-design.md +1 -1
  186. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/media/post-comment-attachments-design.md +26 -27
  187. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/media/upload-pipeline-design.md +28 -30
  188. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/moderation/appeals.md +35 -17
  189. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/moderation/restrictions.md +28 -22
  190. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/notifications/notification_inbox.md +1 -1
  191. core_framework-2.3.0/docs/domains/outbox/admin_outbox_ops.md +182 -0
  192. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/posts/admin_posts.md +2 -2
  193. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/posts/author_context.md +1 -1
  194. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/posts/post_like.md +2 -1
  195. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/posts/post_stats_aggregation.md +8 -7
  196. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/account_deletion.md +2 -1
  197. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/blocks.md +11 -2
  198. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/check_username_exists.md +1 -0
  199. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/follow-system-design.md +69 -61
  200. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/follow.md +8 -4
  201. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/user_removal.md +6 -6
  202. {core_framework-2.1.0 → core_framework-2.3.0}/docs/library/api.md +8 -0
  203. {core_framework-2.1.0 → core_framework-2.3.0}/docs/library/overview.md +5 -3
  204. {core_framework-2.1.0 → core_framework-2.3.0}/docs/library/package-api.md +27 -4
  205. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/README.md +15 -12
  206. core_framework-2.3.0/docs/platform/analytics-rollup-design.md +990 -0
  207. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/architecture-decisions.md +75 -12
  208. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/client-error-visibility.md +1 -1
  209. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/conventions.md +6 -8
  210. core_framework-2.3.0/docs/platform/event-outbox-design.md +722 -0
  211. core_framework-2.3.0/docs/platform/events-ingest-design.md +109 -0
  212. core_framework-2.3.0/docs/platform/outbox-admin-ops-design.md +334 -0
  213. core_framework-2.3.0/docs/todo.md +11 -0
  214. {core_framework-2.1.0 → core_framework-2.3.0}/makefile +2 -2
  215. {core_framework-2.1.0 → core_framework-2.3.0}/pyproject.toml +1 -1
  216. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/_auth.py +8 -5
  217. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/conftest.py +1 -5
  218. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/smoke/tls_host_test.py +24 -6
  219. core_framework-2.3.0/tests/integration/analytics/_helpers.py +135 -0
  220. core_framework-2.3.0/tests/integration/api/_auth_helpers.py +6 -0
  221. core_framework-2.3.0/tests/integration/api/_outbox_helpers.py +138 -0
  222. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/admin/_auth_cases.py +11 -0
  223. core_framework-2.3.0/tests/integration/api/admin/analytics/router_test.py +265 -0
  224. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/admin/cache/router_test.py +5 -25
  225. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/admin/moderation/router_test.py +96 -10
  226. core_framework-2.3.0/tests/integration/api/admin/outbox/router_test.py +333 -0
  227. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/admin/users/router_test.py +5 -25
  228. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/attachments/_helpers.py +3 -17
  229. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/attachments/attachment_upload_test.py +1 -5
  230. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +3 -16
  231. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/comments/public/router_test.py +19 -11
  232. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/create_idempotency_integration_test.py +1 -6
  233. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/events/router_test.py +4 -16
  234. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/notifications/router_test.py +5 -13
  235. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/posts/authenticated/post_writes_integration_test.py +1 -6
  236. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/posts/followers_visibility_test.py +34 -7
  237. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/posts/post_deletion_integration_test.py +97 -7
  238. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/posts/public/router_test.py +6 -16
  239. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/users/authenticated/router_test.py +23 -95
  240. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/users/public/router_test.py +27 -25
  241. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/account_deletion_test.py +3 -15
  242. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/aggregate_comment_stats_test.py +1 -4
  243. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/aggregate_post_stats_test.py +1 -4
  244. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/aggregate_user_stats_test.py +9 -30
  245. core_framework-2.3.0/tests/integration/worker/analytics_prune_test.py +77 -0
  246. core_framework-2.3.0/tests/integration/worker/analytics_refresh_test.py +109 -0
  247. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/create_idempotency_retention_test.py +2 -12
  248. core_framework-2.3.0/tests/integration/worker/moderation_outbox_test.py +306 -0
  249. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/mute_lift_test.py +19 -17
  250. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/stale_staging_sweep_test.py +6 -6
  251. core_framework-2.3.0/tests/integration/worker/user_outbox_test.py +374 -0
  252. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/utils_test.py +1 -6
  253. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/application/events/event_service_test.py +4 -4
  254. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/core/pagination_test.py +7 -16
  255. core_framework-2.3.0/tests/unit/infrastructure/__init__.py +0 -0
  256. core_framework-2.3.0/tests/unit/infrastructure/media/__init__.py +0 -0
  257. {core_framework-2.1.0 → core_framework-2.3.0}/uv.lock +1 -1
  258. core_framework-2.1.0/core_framework/application/events/README.md +0 -26
  259. core_framework-2.1.0/core_framework/application/posts/aggregation_service.py +0 -38
  260. core_framework-2.1.0/docs/domains/README.md +0 -17
  261. core_framework-2.1.0/docs/platform/event-outbox-design.md +0 -529
  262. core_framework-2.1.0/docs/todo.md +0 -10
  263. core_framework-2.1.0/tests/integration/api/_auth_helpers.py +0 -12
  264. {core_framework-2.1.0 → core_framework-2.3.0}/.agents/skills/fastapi +0 -0
  265. {core_framework-2.1.0 → core_framework-2.3.0}/.agents/skills/library-skills/.library-skills.json +0 -0
  266. {core_framework-2.1.0 → core_framework-2.3.0}/.agents/skills/library-skills/SKILL.md +0 -0
  267. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/api-reference-docs.mdc +0 -0
  268. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/api-validation.mdc +0 -0
  269. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/constants-final.mdc +0 -0
  270. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/database-triggers.mdc +0 -0
  271. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/docstrings.mdc +0 -0
  272. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/domain-input-guards.mdc +0 -0
  273. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/domain-services-and-adapters.mdc +0 -0
  274. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/flow-documentation.mdc +0 -0
  275. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/inline-raise-messages.mdc +0 -0
  276. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/integration-test-strategy.mdc +0 -0
  277. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/no-code-in-docs.mdc +0 -0
  278. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/postgres-config-conventions.mdc +0 -0
  279. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/repository-schema-isolation.mdc +0 -0
  280. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/rules/structured-logging.mdc +0 -0
  281. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/skills/add-config/SKILL.md +0 -0
  282. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/skills/fastapi +0 -0
  283. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/skills/recommend-features/SKILL.md +0 -0
  284. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/skills/sync-host-rules/SKILL.md +0 -0
  285. {core_framework-2.1.0 → core_framework-2.3.0}/.cursor/skills/write-flow-doc/SKILL.md +0 -0
  286. {core_framework-2.1.0 → core_framework-2.3.0}/.dockerignore +0 -0
  287. {core_framework-2.1.0 → core_framework-2.3.0}/.github/workflows/dev-ci-cd.yaml +0 -0
  288. {core_framework-2.1.0 → core_framework-2.3.0}/.github/workflows/manual-deployment.yaml +0 -0
  289. {core_framework-2.1.0 → core_framework-2.3.0}/.github/workflows/publish-pypi.yml +0 -0
  290. {core_framework-2.1.0 → core_framework-2.3.0}/.gitignore +0 -0
  291. {core_framework-2.1.0 → core_framework-2.3.0}/.pre-commit-config.yaml +0 -0
  292. {core_framework-2.1.0 → core_framework-2.3.0}/.python-version +0 -0
  293. {core_framework-2.1.0 → core_framework-2.3.0}/LICENSE +0 -0
  294. {core_framework-2.1.0 → core_framework-2.3.0}/README.md +0 -0
  295. {core_framework-2.1.0/alembic/comment → core_framework-2.3.0/alembic/analytics}/alembic/README +0 -0
  296. {core_framework-2.1.0/alembic/comment → core_framework-2.3.0/alembic/analytics}/alembic/script.py.mako +0 -0
  297. {core_framework-2.1.0/alembic/comment → core_framework-2.3.0/alembic/analytics}/alembic.ini +0 -0
  298. {core_framework-2.1.0/alembic/extension → core_framework-2.3.0/alembic/comment}/alembic/README +0 -0
  299. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/comment/alembic/env.py +0 -0
  300. {core_framework-2.1.0/alembic/extension → core_framework-2.3.0/alembic/comment}/alembic/script.py.mako +0 -0
  301. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/comment/alembic/versions/comment_add_comment_attachments.py +0 -0
  302. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency.py +0 -0
  303. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency_created_at_index.py +0 -0
  304. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/comment/alembic/versions/comment_add_comment_mentions.py +0 -0
  305. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/comment/alembic/versions/comment_tombstone_status_model.py +0 -0
  306. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/comment/alembic/versions/v1_comment_init_baseline.py +0 -0
  307. {core_framework-2.1.0/alembic/media → core_framework-2.3.0/alembic/comment}/alembic.ini +0 -0
  308. {core_framework-2.1.0/alembic/media → core_framework-2.3.0/alembic/extension}/alembic/README +0 -0
  309. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/extension/alembic/env.py +0 -0
  310. {core_framework-2.1.0/alembic/media → core_framework-2.3.0/alembic/extension}/alembic/script.py.mako +0 -0
  311. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/extension/alembic/versions/v1_ext_init_baseline.py +0 -0
  312. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/extension/alembic.ini +0 -0
  313. {core_framework-2.1.0/alembic/moderation → core_framework-2.3.0/alembic/media}/alembic/README +0 -0
  314. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/media/alembic/env.py +0 -0
  315. {core_framework-2.1.0/alembic/moderation → core_framework-2.3.0/alembic/media}/alembic/script.py.mako +0 -0
  316. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/media/alembic/versions/media_add_attachment_staging_registry.py +0 -0
  317. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/media/alembic/versions/media_add_banner_staging_and_lease.py +0 -0
  318. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/media/alembic/versions/media_attachment_staging_registry_published_at.py +0 -0
  319. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/media/alembic/versions/v1_media_init_baseline.py +0 -0
  320. {core_framework-2.1.0/alembic/notification → core_framework-2.3.0/alembic/media}/alembic.ini +0 -0
  321. {core_framework-2.1.0/alembic/notification → core_framework-2.3.0/alembic/moderation}/alembic/README +0 -0
  322. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/moderation/alembic/env.py +0 -0
  323. {core_framework-2.1.0/alembic/notification → core_framework-2.3.0/alembic/moderation}/alembic/script.py.mako +0 -0
  324. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +0 -0
  325. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/moderation/alembic.ini +0 -0
  326. {core_framework-2.1.0/alembic/post → core_framework-2.3.0/alembic/notification}/alembic/README +0 -0
  327. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/notification/alembic/env.py +0 -0
  328. {core_framework-2.1.0/alembic/post → core_framework-2.3.0/alembic/notification}/alembic/script.py.mako +0 -0
  329. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/notification/alembic/versions/v1_notif_init_baseline.py +0 -0
  330. {core_framework-2.1.0/alembic/user → core_framework-2.3.0/alembic/post}/alembic/README +0 -0
  331. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/post/alembic/env.py +0 -0
  332. {core_framework-2.1.0/alembic/user → core_framework-2.3.0/alembic/post}/alembic/script.py.mako +0 -0
  333. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/post/alembic/versions/post_add_post_attachments.py +0 -0
  334. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/post/alembic/versions/post_add_post_create_idempotency.py +0 -0
  335. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/post/alembic/versions/post_add_post_create_idempotency_created_at_index.py +0 -0
  336. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/post/alembic/versions/post_add_post_mentions.py +0 -0
  337. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/post/alembic/versions/post_tombstone_status_model.py +0 -0
  338. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/post/alembic/versions/v1_post_init_baseline.py +0 -0
  339. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/post/alembic.ini +0 -0
  340. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/user/alembic/env.py +0 -0
  341. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/user/alembic/versions/user_add_avatar_ingest_sequences.py +0 -0
  342. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/user/alembic/versions/user_add_banner_ingest_sequences.py +0 -0
  343. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/user/alembic/versions/user_add_date_of_birth_to_profiles.py +0 -0
  344. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/user/alembic/versions/user_reserve_sentinel_user_ids.py +0 -0
  345. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/user/alembic/versions/v1_user_init_baseline.py +0 -0
  346. {core_framework-2.1.0 → core_framework-2.3.0}/alembic/user/alembic.ini +0 -0
  347. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/__init__.py +0 -0
  348. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/__init__.py +0 -0
  349. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/__init__.py +0 -0
  350. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/cache/schemas.py +0 -0
  351. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/comments/schemas.py +0 -0
  352. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/moderation/__init__.py +0 -0
  353. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/moderation/schemas.py +0 -0
  354. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/posts/schemas.py +0 -0
  355. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/admin/users/__init__.py +0 -0
  356. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/attachments/router.py +0 -0
  357. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/attachments/schemas.py +0 -0
  358. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/auth/__init__.py +0 -0
  359. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/auth/router.py +0 -0
  360. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/auth/schemas.py +0 -0
  361. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/comments/authenticated/mappers.py +0 -0
  362. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/comments/public/router.py +0 -0
  363. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/comments/public/schemas.py +0 -0
  364. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/comments/router.py +0 -0
  365. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/comments/schemas.py +0 -0
  366. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/constants.py +0 -0
  367. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/dependencies.py +0 -0
  368. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/idempotency/__init__.py +0 -0
  369. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/idempotency/dependencies.py +0 -0
  370. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/notifications/authenticated/schemas.py +0 -0
  371. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/notifications/router.py +0 -0
  372. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/posts/authenticated/mappers.py +0 -0
  373. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/posts/public/router.py +0 -0
  374. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/posts/public/schemas.py +0 -0
  375. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/posts/router.py +0 -0
  376. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/posts/schemas.py +0 -0
  377. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/router.py +0 -0
  378. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/schemas.py +0 -0
  379. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/system/__init__.py +0 -0
  380. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/system/router.py +0 -0
  381. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/__init__.py +0 -0
  382. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/authenticated/__init__.py +0 -0
  383. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/authenticated/mappers.py +0 -0
  384. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/authenticated/schemas.py +0 -0
  385. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/mappers.py +0 -0
  386. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/public/__init__.py +0 -0
  387. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/public/router.py +0 -0
  388. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/public/schemas.py +0 -0
  389. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/router.py +0 -0
  390. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/api/users/shared/schemas.py +0 -0
  391. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/__init__.py +0 -0
  392. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/auth/__init__.py +0 -0
  393. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/auth/access_service.py +0 -0
  394. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/auth/auth_service.py +0 -0
  395. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/auth/models.py +0 -0
  396. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/cache/invalidate_service.py +0 -0
  397. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/cache/models.py +0 -0
  398. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/comments/aggregation_service.py +0 -0
  399. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/comments/cleanup_service.py +0 -0
  400. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/media/attachment_read.py +0 -0
  401. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/media/read_url_config.py +0 -0
  402. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/moderation/__init__.py +0 -0
  403. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/moderation/report_service.py +0 -0
  404. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/moderation/scheduled_service.py +0 -0
  405. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/notifications/README.md +0 -0
  406. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/notifications/enums.py +0 -0
  407. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/notifications/notification_service.py +0 -0
  408. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/posts/admin_service.py +0 -0
  409. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/posts/cleanup_service.py +0 -0
  410. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/shared/__init__.py +0 -0
  411. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/shared/user_agent.py +0 -0
  412. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/shared/worker_jobs.py +0 -0
  413. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/users/__init__.py +0 -0
  414. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/users/aggregation_service.py +0 -0
  415. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/users/deletion_audit.py +0 -0
  416. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/users/enums.py +0 -0
  417. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/application/users/scheduled_service.py +0 -0
  418. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/asgi.py +0 -0
  419. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/constants.py +0 -0
  420. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/__init__.py +0 -0
  421. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/cache.py +0 -0
  422. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/context.py +0 -0
  423. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/exception_handlers/__init__.py +0 -0
  424. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/firebase.py +0 -0
  425. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/http_client.py +0 -0
  426. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/observability.py +0 -0
  427. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/core/redis.py +0 -0
  428. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/__init__.py +0 -0
  429. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/auth/__init__.py +0 -0
  430. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/auth/dependencies.py +0 -0
  431. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/auth/exceptions.py +0 -0
  432. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/auth/models.py +0 -0
  433. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/auth/ports/__init__.py +0 -0
  434. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/auth/ports/auth.py +0 -0
  435. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/__init__.py +0 -0
  436. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/constants.py +0 -0
  437. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/dependencies.py +0 -0
  438. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/enums.py +0 -0
  439. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/exceptions.py +0 -0
  440. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/models.py +0 -0
  441. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/comment/outcomes.py +0 -0
  442. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/exceptions.py +0 -0
  443. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/README.md +0 -0
  444. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/__init__.py +0 -0
  445. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/components/staging.py +0 -0
  446. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/components/variant_encoder.py +0 -0
  447. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/constants.py +0 -0
  448. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/exceptions.py +0 -0
  449. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/models.py +0 -0
  450. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/ports/staging.py +0 -0
  451. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/attachment/ports/variant_encoder.py +0 -0
  452. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/__init__.py +0 -0
  453. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/components/__init__.py +0 -0
  454. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/components/finals_publisher.py +0 -0
  455. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/components/staging.py +0 -0
  456. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/components/variant_encoder.py +0 -0
  457. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/constants.py +0 -0
  458. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/exceptions.py +0 -0
  459. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/models.py +0 -0
  460. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/ports/__init__.py +0 -0
  461. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/ports/finals_publisher.py +0 -0
  462. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/ports/staging.py +0 -0
  463. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/avatar/ports/variant_encoder.py +0 -0
  464. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/components/finals_publisher.py +0 -0
  465. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/components/staging.py +0 -0
  466. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/components/variant_encoder.py +0 -0
  467. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/constants.py +0 -0
  468. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/exceptions.py +0 -0
  469. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/models.py +0 -0
  470. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/ports/finals_publisher.py +0 -0
  471. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/ports/staging.py +0 -0
  472. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/banner/ports/variant_encoder.py +0 -0
  473. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/dependencies.py +0 -0
  474. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/shared/__init__.py +0 -0
  475. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/shared/constants.py +0 -0
  476. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/shared/exceptions.py +0 -0
  477. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/shared/outcomes.py +0 -0
  478. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/shared/service.py +0 -0
  479. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/media/shared/utils.py +0 -0
  480. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/moderation/README.md +0 -0
  481. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/moderation/__init__.py +0 -0
  482. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/moderation/enums.py +0 -0
  483. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/moderation/exceptions.py +0 -0
  484. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/moderation/models.py +0 -0
  485. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/moderation/outcomes.py +0 -0
  486. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/notification/README.md +0 -0
  487. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/notification/__init__.py +0 -0
  488. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/notification/dependencies.py +0 -0
  489. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/notification/enums.py +0 -0
  490. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/notification/exceptions.py +0 -0
  491. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/notification/models.py +0 -0
  492. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/notification/repository.py +0 -0
  493. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/notification/service.py +0 -0
  494. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/__init__.py +0 -0
  495. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/constants.py +0 -0
  496. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/dependencies.py +0 -0
  497. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/enums.py +0 -0
  498. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/exceptions.py +0 -0
  499. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/post/outcomes.py +0 -0
  500. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/README.md +0 -0
  501. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/__init__.py +0 -0
  502. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/constants.py +0 -0
  503. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/enums.py +0 -0
  504. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/exceptions.py +0 -0
  505. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/models.py +0 -0
  506. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/domains/user/utils.py +0 -0
  507. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/__init__.py +0 -0
  508. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/firebase/__init__.py +0 -0
  509. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/firebase/firebase_auth_adapter.py +0 -0
  510. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/__init__.py +0 -0
  511. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/attachment_staging_display_geometry.py +0 -0
  512. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/local_attachment_staging_adapter.py +0 -0
  513. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/local_banner_staging_adapter.py +0 -0
  514. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/local_staging_adapter.py +0 -0
  515. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/pillow_attachment_variant_encoder_adapter.py +0 -0
  516. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/pillow_avatar_variant_encoder_adapter.py +0 -0
  517. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/pillow_banner_variant_encoder_adapter.py +0 -0
  518. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/infrastructure/media/pillow_staging_image.py +0 -0
  519. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/main.py +0 -0
  520. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/migrate_cli.py +0 -0
  521. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/__init__.py +0 -0
  522. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/config.py +0 -0
  523. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/firebase.py +0 -0
  524. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/hookspecs.py +0 -0
  525. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/httpx_test_client.py +0 -0
  526. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/migrations.py +0 -0
  527. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/testing/plugin.py +0 -0
  528. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/__init__.py +0 -0
  529. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +0 -0
  530. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/schedule_aggregate_post_stats.py +0 -0
  531. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/schedule_aggregate_user_stats.py +0 -0
  532. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/schedule_expired_account_deletions.py +0 -0
  533. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/schedule_expired_mute_lifts.py +0 -0
  534. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/schedule_purge_expired_create_idempotency_keys.py +0 -0
  535. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/schedules/schedule_sweep_stale_attachment_staging.py +0 -0
  536. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/delete_attachment_finals.py +0 -0
  537. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/delete_superseded_avatar_finals.py +0 -0
  538. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/delete_superseded_banner_finals.py +0 -0
  539. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_account_deletion.py +0 -0
  540. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_admin_user_deletion.py +0 -0
  541. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_aggregate_comment_stats.py +0 -0
  542. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_aggregate_post_stats.py +0 -0
  543. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_aggregate_user_stats.py +0 -0
  544. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_banner_asset_variants.py +0 -0
  545. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_cache_pattern_invalidation.py +0 -0
  546. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_destructive_comment_delete.py +0 -0
  547. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_destructive_post_delete.py +0 -0
  548. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_media_asset_variants.py +0 -0
  549. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_mute_lift.py +0 -0
  550. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/tasks/process_user_content_deletion.py +0 -0
  551. {core_framework-2.1.0 → core_framework-2.3.0}/core_framework/worker/worker_context.py +0 -0
  552. {core_framework-2.1.0 → core_framework-2.3.0}/docker-compose.dev.yaml +0 -0
  553. {core_framework-2.1.0 → core_framework-2.3.0}/docker-compose.yml +0 -0
  554. {core_framework-2.1.0 → core_framework-2.3.0}/dockerfile +0 -0
  555. {core_framework-2.1.0 → core_framework-2.3.0}/docs/README.md +0 -0
  556. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/README.md +0 -0
  557. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/edge-upload-limits.md +0 -0
  558. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/examples/Caddyfile.example +0 -0
  559. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/examples/image-server-known-hosts.example +0 -0
  560. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/guides/app-server-deploy-ssh.md +0 -0
  561. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/guides/image-server-ssh.md +0 -0
  562. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/guides/postgres-setup.md +0 -0
  563. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/guides/redis-setup.md +0 -0
  564. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/guides/ubuntu-server-setup.md +0 -0
  565. {core_framework-2.1.0 → core_framework-2.3.0}/docs/deployments/release-playbook.md +0 -0
  566. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/auth/registration.md +0 -0
  567. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/comments/comment_report.md +0 -0
  568. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/comments/comment_stats_aggregation.md +0 -0
  569. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/comments/create_comment.md +0 -0
  570. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/comments/delete_comment.md +0 -0
  571. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/comments/edit_comment.md +0 -0
  572. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/comments/retrieve_comments.md +0 -0
  573. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/media/upload_pipeline.md +0 -0
  574. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/mentions/mentions_in_content.md +0 -0
  575. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/moderation/internal_notes.md +0 -0
  576. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/moderation/moderator_actions.md +0 -0
  577. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/moderation/reports.md +0 -0
  578. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/posts/hashtag_discovery.md +0 -0
  579. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/posts/post_visibility.md +0 -0
  580. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/account.md +0 -0
  581. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/avatar.md +0 -0
  582. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/banner.md +0 -0
  583. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/change_history.md +0 -0
  584. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/my_posts_and_comments.md +0 -0
  585. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/preferences.md +0 -0
  586. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/profile.md +0 -0
  587. {core_framework-2.1.0 → core_framework-2.3.0}/docs/domains/users/public_profile.md +0 -0
  588. {core_framework-2.1.0 → core_framework-2.3.0}/docs/library/README.md +0 -0
  589. {core_framework-2.1.0 → core_framework-2.3.0}/docs/library/core-framework-migration.md +0 -0
  590. {core_framework-2.1.0 → core_framework-2.3.0}/docs/library/testing-plugin-design.md +0 -0
  591. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/create-idempotency-design.md +0 -0
  592. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/database-triggers.md +0 -0
  593. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/domain-services-and-adapters.md +0 -0
  594. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/layers-and-boundaries.md +0 -0
  595. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/media-storage-topology.md +0 -0
  596. {core_framework-2.1.0 → core_framework-2.3.0}/docs/platform/patch-update-typed-payload.md +0 -0
  597. {core_framework-2.1.0 → core_framework-2.3.0}/firebase_config.example.json +0 -0
  598. {core_framework-2.1.0 → core_framework-2.3.0}/tests/__init__.py +0 -0
  599. {core_framework-2.1.0 → core_framework-2.3.0}/tests/conftest.py +0 -0
  600. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/_canaries.py +0 -0
  601. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/_media.py +0 -0
  602. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/_settings.py +0 -0
  603. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/_upload_payloads.py +0 -0
  604. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/media/pipeline_test.py +0 -0
  605. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/smoke/canary_test.py +0 -0
  606. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/smoke/media_test.py +0 -0
  607. {core_framework-2.1.0 → core_framework-2.3.0}/tests/e2e/smoke/system_test.py +0 -0
  608. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/__init__.py +0 -0
  609. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/_http_error_details.py +0 -0
  610. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/__init__.py +0 -0
  611. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/_destructive_delete_helpers.py +0 -0
  612. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/_http_helpers.py +0 -0
  613. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/_user_deletion_helpers.py +0 -0
  614. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/_user_helpers.py +0 -0
  615. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/admin/__init__.py +0 -0
  616. {core_framework-2.1.0/tests/integration/api/admin/cache → core_framework-2.3.0/tests/integration/api/admin/analytics}/__init__.py +0 -0
  617. {core_framework-2.1.0/tests/integration/api/admin/comments → core_framework-2.3.0/tests/integration/api/admin/cache}/__init__.py +0 -0
  618. {core_framework-2.1.0/tests/integration/api/admin/moderation → core_framework-2.3.0/tests/integration/api/admin/comments}/__init__.py +0 -0
  619. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/admin/comments/router_test.py +0 -0
  620. {core_framework-2.1.0/tests/integration/api/admin/posts → core_framework-2.3.0/tests/integration/api/admin/moderation}/__init__.py +0 -0
  621. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/admin/moderation/_auth_cases.py +0 -0
  622. {core_framework-2.1.0/tests/integration/api/admin/users → core_framework-2.3.0/tests/integration/api/admin/outbox}/__init__.py +0 -0
  623. {core_framework-2.1.0/tests/integration/api/auth → core_framework-2.3.0/tests/integration/api/admin/posts}/__init__.py +0 -0
  624. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/admin/posts/router_test.py +0 -0
  625. {core_framework-2.1.0/tests/integration/api/comments → core_framework-2.3.0/tests/integration/api/admin/users}/__init__.py +0 -0
  626. {core_framework-2.1.0/tests/integration/api/comments/authenticated → core_framework-2.3.0/tests/integration/api/auth}/__init__.py +0 -0
  627. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/auth/router_test.py +0 -0
  628. {core_framework-2.1.0/tests/integration/api/comments/public → core_framework-2.3.0/tests/integration/api/comments}/__init__.py +0 -0
  629. {core_framework-2.1.0/tests/integration/api/notifications → core_framework-2.3.0/tests/integration/api/comments/authenticated}/__init__.py +0 -0
  630. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/comments/comment_attachments_integration_test.py +0 -0
  631. {core_framework-2.1.0/tests/integration/api/posts → core_framework-2.3.0/tests/integration/api/comments/public}/__init__.py +0 -0
  632. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/media/ingest_contract_test.py +0 -0
  633. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/mentions/mentions_integration_test.py +0 -0
  634. {core_framework-2.1.0/tests/integration/api/posts/public → core_framework-2.3.0/tests/integration/api/notifications}/__init__.py +0 -0
  635. {core_framework-2.1.0/tests/integration/api/system → core_framework-2.3.0/tests/integration/api/posts}/__init__.py +0 -0
  636. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/posts/comment_count_aggregation_test.py +0 -0
  637. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/posts/post_attachments_integration_test.py +0 -0
  638. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/posts/post_stats_dirty_marking_test.py +0 -0
  639. {core_framework-2.1.0/tests/integration/api/users → core_framework-2.3.0/tests/integration/api/posts/public}/__init__.py +0 -0
  640. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/public_read_by_id_unavailable_detail_test.py +0 -0
  641. {core_framework-2.1.0/tests/integration/api/users/authenticated → core_framework-2.3.0/tests/integration/api/system}/__init__.py +0 -0
  642. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/system/router_test.py +0 -0
  643. {core_framework-2.1.0/tests/integration/api/users/public → core_framework-2.3.0/tests/integration/api/users}/__init__.py +0 -0
  644. {core_framework-2.1.0/tests/integration/worker → core_framework-2.3.0/tests/integration/api/users/authenticated}/__init__.py +0 -0
  645. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/users/authenticated/avatar_upload_test.py +0 -0
  646. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/users/authenticated/banner_upload_test.py +0 -0
  647. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/users/deletion_audit_integration_test.py +0 -0
  648. {core_framework-2.1.0/tests/unit → core_framework-2.3.0/tests/integration/api/users/public}/__init__.py +0 -0
  649. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/api/users/user_deletion_integration_test.py +0 -0
  650. {core_framework-2.1.0/tests/unit/application/comments → core_framework-2.3.0/tests/integration/worker}/__init__.py +0 -0
  651. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/_media_upload_helpers.py +0 -0
  652. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/attachment_upload_test.py +0 -0
  653. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/avatar_upload_test.py +0 -0
  654. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/banner_upload_test.py +0 -0
  655. {core_framework-2.1.0 → core_framework-2.3.0}/tests/integration/worker/conftest.py +0 -0
  656. {core_framework-2.1.0/tests/unit/domains → core_framework-2.3.0/tests/unit}/__init__.py +0 -0
  657. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/api/comments/authenticated/comment_request_schemas_test.py +0 -0
  658. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/api/dedupe_preserving_order_test.py +0 -0
  659. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/api/posts/authenticated/post_update_mapper_test.py +0 -0
  660. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/api/users/profile_update_mapper_test.py +0 -0
  661. {core_framework-2.1.0/tests/unit/domains/comment → core_framework-2.3.0/tests/unit/application/comments}/__init__.py +0 -0
  662. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/application/notifications/inbox_service_test.py +0 -0
  663. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/application/users/deletion_audit_test.py +0 -0
  664. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/core/bundled_alembic_test.py +0 -0
  665. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/core/migrate_cli_test.py +0 -0
  666. {core_framework-2.1.0/tests/unit/infrastructure → core_framework-2.3.0/tests/unit/domains}/__init__.py +0 -0
  667. {core_framework-2.1.0/tests/unit/infrastructure/media → core_framework-2.3.0/tests/unit/domains/comment}/__init__.py +0 -0
  668. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/domains/post/models_test.py +0 -0
  669. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/domains/user/service_test.py +0 -0
  670. {core_framework-2.1.0 → core_framework-2.3.0}/tests/unit/infrastructure/media/ssh_finals_publisher_adapter_test.py +0 -0
@@ -1,14 +1,16 @@
1
1
  ---
2
- description: Alembic revision id format short random hex, not descriptive slugs
2
+ description: Alembic migrations — revision id format and index DDL conventions
3
3
  globs: alembic/**/alembic/versions/*.py
4
4
  alwaysApply: false
5
5
  ---
6
6
 
7
- # Alembic revision IDs
7
+ # Alembic migrations
8
+
9
+ ## Revision IDs
8
10
 
9
11
  `alembic_version.version_num` is a **32-character** string. Revision ids must stay well under that limit.
10
12
 
11
- ## New revisions (required)
13
+ ### New revisions (required)
12
14
 
13
15
  - Set `revision` to a **12-character lowercase hex** string (Alembic default), e.g. `a56bc6b7f799`.
14
16
  - Generate with `python -c "import secrets; print(secrets.token_hex(6))"` or `alembic revision`.
@@ -30,10 +32,29 @@ revision: str = "120bf6be81eb"
30
32
  revision: str = "post_tombstone_status_model"
31
33
  ```
32
34
 
33
- ## Exceptions
35
+ ### Exceptions
34
36
 
35
37
  - Collapsed **`v1_*_init`** baseline revisions keep their stable names (`v1_post_init`, etc.) — do not rename shipped baselines.
36
38
 
37
- ## Do not rename shipped revision ids
39
+ ### Do not rename shipped revision ids
38
40
 
39
41
  If a revision may already be applied in any environment, **do not** change its `revision` string. Add a forward migration instead.
42
+
43
+ ## Index DDL
44
+
45
+ ### New index migrations (required)
46
+
47
+ In **`upgrade()`**, create indexes with **`IF NOT EXISTS`** so migrations succeed when an index was already applied manually in production:
48
+
49
+ ```python
50
+ op.execute("""
51
+ create index if not exists idx_example_created_at
52
+ on example_table (created_at);
53
+ """)
54
+ ```
55
+
56
+ Use **`drop index if exists`** in **`downgrade()`**.
57
+
58
+ ### Do not retrofit index DDL
59
+
60
+ Do **not** edit shipped migrations only to add `if not exists` — those revisions may already be applied. New forward migrations follow this rule; legacy `create index` without `if not exists` stays as-is.
@@ -6,7 +6,7 @@ globs:
6
6
 
7
7
  # API layer (core-framework)
8
8
 
9
- **Framework patterns:** use the **fastapi** skill (`.cursor/skills/fastapi/`) — `Annotated`, dependencies, routers, Pydantic v2, CLI.
9
+ **Framework patterns:** use the **fastapi** skill (`.agents/skills/fastapi/`) — `Annotated`, dependencies, routers, Pydantic v2, CLI.
10
10
 
11
11
  **Validation:** **`api-validation.mdc`**. **Errors:** **`error-boundary.mdc`**. **Domain imports:** **`domain-imports.mdc`**.
12
12
 
@@ -18,7 +18,7 @@ Application services orchestrate use cases shared by API and workers. They stay
18
18
 
19
19
  - Return plain Python/domain values: `dict`, `list[dict]`, dataclasses, primitives, `None`, or empty collections.
20
20
  - Do not return API/Pydantic `BaseModel` instances.
21
- - Signal HTTP-meaningful failures with **`application.shared.exceptions`** or plain outcomes (`None`, empty), never typed domain exceptions. See **`error-boundary.mdc`**.
21
+ - Signal HTTP-meaningful failures with **`application.shared.exceptions`** or plain outcomes (`None`, empty), never typed domain exceptions. Full boundary contract: **`error-boundary.mdc`**.
22
22
 
23
23
  Use a domain dataclass when one domain row matches the response shape; use a `dict` when the payload is composed from several sources.
24
24
 
@@ -10,6 +10,7 @@ alwaysApply: true
10
10
  - State assumptions; ask when unclear. Prefer the simplest correct solution.
11
11
  - Touch only what the task requires. Match existing style. Remove orphans your edits create.
12
12
  - Define verifiable success criteria for non-trivial work.
13
+ - **Inline small single-use helpers** — do not extract a private function used once and only a few lines (identifier quoting, a short SQL fragment, pass-through wrappers). Extract when the logic is **reused**, **~15+ lines / non-obvious branching**, or a scoped rule requires sharing (e.g. normal + `*_strong` repository reads in **`strong-reads.mdc`**). Do not repo-wide refactor unrelated existing helpers.
13
14
 
14
15
  ## Tech stack
15
16
 
@@ -25,7 +26,12 @@ Dependencies point inward: API/worker → application → domain. Never reverse.
25
26
  - **Application** — orchestration shared by API + worker; no FastAPI types.
26
27
  - **Domain** — models, repositories (Postgres only), services. No API/worker/application imports except wiring in `dependencies.py`.
27
28
 
28
- Path-scoped rules under `.cursor/rules/` add detail (API, domains, docs, etc.). For FastAPI/Pydantic work in `core_framework/api/`, use the **fastapi** skill (`.cursor/skills/fastapi/`).
29
+ Path-scoped rules under `.cursor/rules/` add detail (API, domains, docs, etc.). For FastAPI/Pydantic work in `core_framework/api/`, use the **fastapi** skill (`.agents/skills/fastapi/`).
30
+
31
+ ## Agent skills
32
+
33
+ - Package skills (for example **fastapi**) are Library Skills-managed symlinks under **`.agents/skills/<name>/`**. Rules and hand-authored skills reference that path, not a copied tree under **`.cursor/skills/`**.
34
+ - **`.cursor/skills/<name>`** may symlink to **`.agents/skills/<name>`** for Cursor; Library Skills repairs **`.agents/skills`** links when packages move. After **`uv sync`** or a dependency upgrade, run **`uvx library-skills --yes`** if symlinks are stale.
29
35
 
30
36
  ## Host applications
31
37
 
@@ -34,7 +34,7 @@ Domain layer names must describe domain concepts and data semantics, not who cal
34
34
  - `tombstone_post` ✅
35
35
  - `detach_post_authors_by_author_id` ✅
36
36
  - Prefer naming by data shape or business rule, not persona:
37
- - `select_post_with_metadata_by_id` ✅
37
+ - `select_post_with_metadata_by_id_if_exists` ✅
38
38
  - `retrieve_user_for_detail` ✅
39
39
  - Keep caller-specific naming in API/application layers only.
40
40
 
@@ -16,7 +16,7 @@ When importing from **`core_framework/domains/*`** in API or application code:
16
16
 
17
17
  ## Prohibited
18
18
 
19
- - Typed or base **domain exceptions** in API/application — use **`application.shared.exceptions`**.
19
+ - Typed or base **domain exceptions** in API/application — use **`application.shared.exceptions`**. Full policy: **`error-boundary.mdc`**.
20
20
  - Direct imports from internal modules (`models`, `enums`, `repository`, `service`, `exceptions`).
21
21
 
22
22
  ```python
@@ -41,4 +41,4 @@ To see what a domain exports, check its **`__init__.py`** **`__all__`**.
41
41
  2. Raise an **application** exception when HTTP must signal failure.
42
42
  3. Extend the domain service API to absorb typed exceptions internally.
43
43
 
44
- Application code never imports repositories. Exception boundary details: **`error-boundary.mdc`**.
44
+ Application code never imports repositories. Exception boundary: **`error-boundary.mdc`**.
@@ -6,15 +6,18 @@ globs:
6
6
 
7
7
  # Domain: repository exceptions vs service
8
8
 
9
+ Canonical HTTP/application boundary: **`error-boundary.mdc`**. Application and API must not import or handle typed domain exceptions.
10
+
9
11
  ## Thin service + single persistence step
10
12
 
11
- When a **`Service`** method in **`core_framework/domains/*/service.py`** only performs **shared input guards** (e.g. **`require_*`**, **`validate_pagination_limit`**) **and** delegates to **one** repository call with **no branching** afterward, **prefer raising domain exceptions inside the repository** when the persistence outcome is an error condition (empty `RETURNING`, unexpected row absence on insert).
13
+ When a **`Service`** method in **`core_framework/domains/*/service.py`** only performs **shared input guards** (e.g. **`require_*`**, **`validate_pagination_limit`**) **and** delegates to **one** repository call with **no branching** afterward, the repository may raise typed domain exceptions for persistence error outcomes (empty `RETURNING`, unexpected row absence on insert).
12
14
 
13
- The service stays a façade: validate arguments, then **`await`** the repository method and let **domain exceptions** propagate.
15
+ That propagation is **intra-domain only**. Methods that **application** or **workers** call must **not** leak typed exceptions outward — absorb them inside the service (return **`None`**, outcomes, or **`bool`**) or branch before re-raising. See **`error-boundary.mdc`**.
14
16
 
15
17
  ## Prefer service-side translation when
16
18
 
17
19
  - The service **composes multiple** repository calls, **branches** on outcomes, or maps the same SQL outcome to **different domain errors**.
20
+ - The method is an **application-callable** entrypoint (anything imported via **`dependencies.py`** and used from **`core_framework/application/`** or **`core_framework/api/`**).
18
21
  - Keeping the repository purely “data shaped” (**`Optional`** / **`bool`**) materially improves reuse or clarity for **multiple** service entrypoints calling the same repository method.
19
22
 
20
23
  Existing domains may mix both styles; use this guide for **new work** and when **refactoring** a call site for clarity.
@@ -2,8 +2,9 @@
2
2
  description: HTTP error boundaries — application vs domain exceptions, handlers, anti-enumeration
3
3
  globs:
4
4
  - core_framework/core/exception_handlers/**/*.py
5
- - core_framework/application/**/*_service.py
5
+ - core_framework/application/**/*.py
6
6
  - core_framework/api/**/router.py
7
+ - core_framework/api/**/schemas.py
7
8
  - core_framework/domains/**/exceptions.py
8
9
  ---
9
10
 
@@ -15,7 +16,7 @@ Platform contract: **`docs/platform/client-error-visibility.md`**. Domain import
15
16
 
16
17
  | Layer | Outward errors | HTTP |
17
18
  |-------|----------------|------|
18
- | **Application** | **`application.shared.exceptions`** | Handlers map type → status + `detail` |
19
+ | **Application** | **`application.shared.exceptions`**, **`InvalidPageTokenError`** | Handlers map type → status + `detail` |
19
20
  | **Domain typed** | Internal — not imported by API/application | No typed handlers |
20
21
  | **Domain `BaseXxxException`** | Handler catch-all only | **500** if leaked |
21
22
  | **API** | Pydantic | **422** |
@@ -30,7 +31,7 @@ Platform contract: **`docs/platform/client-error-visibility.md`**. Domain import
30
31
  ## Handler registration
31
32
 
32
33
  - Use **`register_exception_handler`** from **`core_framework.core.exception_handlers.common`** — not raw **`app.add_exception_handler`** elsewhere.
33
- - **`setup.py`**: application types (specific before any base), **`DomainValidationError`**, one **`BaseDomainException`** leak handler, then **`Exception`** unmapped.
34
+ - Register handlers in **`core_framework/core/exception_handlers/setup.py`** and **`application.py`**: application types (specific before any base), **`InvalidPageTokenError`**, **`DomainValidationError`**, one **`BaseDomainException`** leak handler, then **`Exception`** unmapped.
34
35
  - No per-domain handler modules for typed exceptions. Handlers translate only — no business logic or repository calls.
35
36
  - Do not log **error** for expected application 4xx; **`DomainValidationError`** at **warning**/**info**.
36
37
 
@@ -18,7 +18,7 @@ Dependencies point inward: **API/worker → application → domain**. Never reve
18
18
 
19
19
  ## Cross-layer rules
20
20
 
21
- - Worker code must not import `core_framework.domains` directly, except documented housekeeping cron handlers that call a configured domain facade.
21
+ - Worker code must not import `core_framework.domains` directly; call **application** services from worker tasks and schedules.
22
22
  - Domain code imports global infrastructure only in `dependencies.py`.
23
23
  - Repositories are Postgres-only and must not query other domains' schemas. See **`repository-schema-isolation`**.
24
24
  - Non-SQL I/O uses domain ports/components plus infrastructure adapters. See **`domain-services-and-adapters`**.
@@ -4,7 +4,7 @@ globs:
4
4
  - core_framework/domains/**/repository.py
5
5
  - core_framework/domains/**/service.py
6
6
  - core_framework/domains/**/dependencies.py
7
- - core_framework/application/**/*_service.py
7
+ - core_framework/application/**/*.py
8
8
  ---
9
9
 
10
10
  # Strong reads
@@ -13,6 +13,7 @@ globs:
13
13
 
14
14
  - **Read-after-write** in the same HTTP request or worker job → **strong** read (required). Refetch with `strong_read_database`, `*_strong` repository methods, or service accessors that call them.
15
15
  - **Decision-critical guards** where stale data can cause incorrect branching → **strong** read (required). These are reads performed to decide a branch (for example pre-write existence, role, or state checks that determine whether a write runs or which outcome is returned), even when there is no immediate prior write in the flow. See the "Read Consistency (Strong vs Normal Reads)" ADR.
16
+ - **Accepted exception:** **`check_not_banned`** / **`get_user_detail`** (`USER_DETAIL` cache + normal reads) — ban visibility is eventually consistent by design. See **`docs/platform/architecture-decisions.md`** → *Ban enforcement via cached `USER_DETAIL` is eventually consistent*. Do not flag as a strong-read defect unless that ADR is updated.
16
17
  - **All other strong-read usage** (e.g., list/browse freshness) → opt-in unless the user explicitly requested it in the task.
17
18
  - If a flow writes then reads and only a normal-read API exists, add or extend the `*_strong` path and call it from that flow.
18
19
  - Repositories accept `strong_read_database: Postgres` on `__init__` for wiring symmetry even when unused.
@@ -21,9 +22,8 @@ globs:
21
22
 
22
23
  - Normal reads: explicit methods (e.g. `select_appeal`) using `read_database`.
23
24
  - Strong reads: separate methods only where needed (e.g. `select_appeal_strong`) using `strong_read_database`.
24
- - Share one private helper that accepts a database handle — do not duplicate SQL or mapping.
25
- - If only strong-read behavior is needed today, use a single method with `_strong` in the name.
26
- - Service accessors that call strong repository methods should also include `_strong`.
25
+ - Share one private helper that accepts a database handle when **both** normal and strong public methods exist — do not duplicate SQL or mapping. If only a `*_strong` method exists today, keep SQL inline in that method (constitution → inline small single-use helpers).
26
+ - Service accessors that call strong repository methods should use **`retrieve_*_strong`** naming (repositories keep **`select_*_strong`**).
27
27
 
28
28
  ```python
29
29
  # Preferred
@@ -192,7 +192,7 @@ Worker `startup` and API `init_app` already call `configure_application_dependen
192
192
 
193
193
  Do this in a **separate step/PR** when you have DDL to apply.
194
194
 
195
- 1. Add **`alembic/<domain>/alembic/versions/*.py`** (at least one revision); `upgrade()` may create schema/tables/enums as designed. Use a **12-character lowercase hex** `revision` id (see **`alembic-revision-ids`**); put the descriptive slug in the filename only.
195
+ 1. Add **`alembic/<domain>/alembic/versions/*.py`** (at least one revision); `upgrade()` may create schema/tables/enums as designed. Use a **12-character lowercase hex** `revision` id (see **`alembic-migrations`**); put the descriptive slug in the filename only.
196
196
  1. **Append** `<domain>` to `ALEMBIC_DOMAINS` in `core_framework/bundled_alembic.py` **in the same change** as the first revision (so `cf-alembic` / CI stay green).
197
197
  1. `make alembic` and deploy’s `cf-alembic` step pick up the new domain automatically — no makefile or `_deploy.yml` edits needed.
198
198
 
@@ -25,7 +25,7 @@ Do **not** flag documented accepted trade-offs in **`docs/platform/architecture-
25
25
  ## Quick checks
26
26
 
27
27
  - Application services: no HTTP types, no domain exceptions, no Pydantic return types, one-line docstrings.
28
- - API: thin routers, validation in schemas, orchestration in application — **fastapi** skill + **`api-layer`** / **`api-validation`** / **`error-boundary`** rules.
28
+ - API: thin routers, validation in schemas, orchestration in application — **fastapi** skill (`.agents/skills/fastapi/`) + **`api-layer`** / **`api-validation`** / **`error-boundary`** rules.
29
29
  - Domain: imports only via **`__init__.py`** / **`dependencies`** from outer layers; no caller-context names (**`domain-caller-context`**).
30
30
  - Read-after-write refetches use **`*_strong`** where applicable.
31
31
 
@@ -24,7 +24,7 @@ Implement **one step at a time**. Stop after each step and wait for user review.
24
24
  After the user accepts database changes (or if there were none):
25
25
 
26
26
  - **Only** implement the API layer: schemas and router.
27
- - Follow the **fastapi** skill and **`api-layer`** / **`api-validation`** / **`error-boundary`** rules.
27
+ - Follow the **fastapi** skill (`.agents/skills/fastapi/`) and **`api-layer`** / **`api-validation`** / **`error-boundary`** rules.
28
28
  - **No** changes in domain, service, or application layers.
29
29
  - Include request/response schemas, error handling, and HTTP-related logic.
30
30
  - If the feature is not yet implemented, return 501 Not Implemented from the route.
@@ -55,10 +55,13 @@ jobs:
55
55
  IMAGE_SERVER_PORT: ${{ vars.IMAGE_SERVER_PORT }}
56
56
  IMAGE_SERVER_USER: ${{ vars.IMAGE_SERVER_USER }}
57
57
  LOGFIRE_TOKEN: ${{ secrets.LOGFIRE_TOKEN }}
58
+ OBSERVABILITY_ENABLED: ${{ vars.OBSERVABILITY_ENABLED }}
58
59
  PROFILING_KEY: ${{ vars.PROFILING_KEY }}
59
60
  PROFILING_ENABLED: ${{ vars.PROFILING_ENABLED }}
60
61
  LOG_LEVEL: ${{ vars.LOG_LEVEL }}
61
62
  CORS_ALLOWED_ORIGINS: ${{ vars.CORS_ALLOWED_ORIGINS }}
63
+ RETENTION_POST_VIEWS_DAYS: ${{ vars.RETENTION_POST_VIEWS_DAYS }}
64
+ ANALYTICS_REFRESH_INTERVAL_SECONDS: ${{ vars.ANALYTICS_REFRESH_INTERVAL_SECONDS }}
62
65
  run: |
63
66
  set -euo pipefail
64
67
 
@@ -32,19 +32,14 @@ jobs:
32
32
  run: uv run ruff check
33
33
 
34
34
  - name: Install Firebase CLI
35
- run: npm install -g firebase-tools
36
-
37
- - name: Start Firebase Emulator
38
- run: |
39
- firebase emulators:start --only auth --project=test &
40
- sleep 5
41
- env:
42
- FIREBASE_AUTH_EMULATOR_HOST: localhost:9099
35
+ run: npm install -g firebase-tools@15.20.0
43
36
 
44
37
  - name: pytest
45
- run: uv run pytest tests/unit tests/integration -n 2
38
+ run: |
39
+ firebase --version
40
+ uv run pytest tests/unit tests/integration -n 2
46
41
  env:
47
- FIREBASE_AUTH_EMULATOR_HOST: localhost:9099
42
+ FIREBASE_AUTH_EMULATOR_HOST: 127.0.0.1:9099
48
43
 
49
44
  package-smoke:
50
45
  runs-on: ubuntu-latest
@@ -4,6 +4,56 @@ Notable changes to **core-framework** (import **`core_framework`**). Format foll
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.3.0] - 2026-07-03
8
+
9
+ Analytics rollup domain (materialized daily activity counts), admin read API, and retention prune. Hosts must add **`[analytics]`** and **`schema_analytics`**, run **`cf-alembic`**, and run the worker for rollup refresh and daily-fact prune.
10
+
11
+ ### Added
12
+
13
+ - **Analytics rollup (Phase 1):** `analytics` domain scaffold, `schema_analytics`, `[analytics].refresh_interval_seconds` (required on **`Settings`**; core **`config.toml`** ships **300**), Alembic tree with `daily_fact` table (PK only), and activity source registry shell. See **`docs/platform/analytics-rollup-design.md`**.
14
+ - **Analytics rollup (Phase 2):** `AnalyticsService.list_refreshable_days` and `is_final_bucket_refresh` (planner inputs), `refresh_daily_fact_bucket` (single-bucket executor), optional `refresh_all_open_buckets` for tests; transactional `replace_daily_fact_bucket`; admin read helpers.
15
+ - **Analytics rollup (Phase 3):** `post` / `created` activity source, planner (`enqueue_refresh_analytics_buckets`) and executor (`refresh_analytics_bucket`) application entrypoints, `schedule_refresh_analytics` + `process_refresh_analytics_bucket` worker (cron from `refresh_interval_seconds`).
16
+ - **Analytics rollup (Phase 4):** `comment` / `created`, `user` / `registered`, and `post` / `viewed` activity sources with domain UTC-day count accessors and registry registration.
17
+ - **Analytics rollup (Phase 5):** admin read API — `GET /admin/analytics/daily/range` and `GET /admin/analytics/daily/day` via `read_service` and Phase 2 domain read helpers.
18
+ - **Analytics rollup (Phase 6):** one-year `daily_fact` retention prune (`schedule_prune_analytics_daily_facts`, daily **05:10 UTC**); `idx_daily_fact_day` migration. Source-feed `created_at` indexes for refresh counts already exist on `posts`, `comments`, `users`, and `post_views`.
19
+ - **Analytics rollup (Phase 7):** integration tests for refresh planner/executor, admin read API, and retention prune.
20
+
21
+ ### Breaking
22
+
23
+ - **Breaking (hosts — config):** Add **`[analytics]`** with **`refresh_interval_seconds`** (required on **`Settings`**; core **`config.toml`** ships **300**). Deploy variable **`ANALYTICS_REFRESH_INTERVAL_SECONDS`**. Add **`schema_analytics`** under **`[postgres_schemas]`** (default **`analytics`**).
24
+ - **Breaking (hosts — migration):** When upgrading to a release that includes analytics rollup, run **`cf-alembic`** so the **`analytics`** migration stream applies **`analytics_daily_fact`** (`analytics.daily_fact`) and **`analytics_daily_fact_day_index`** (`idx_daily_fact_day`). The worker must run **`schedule_refresh_analytics`** and **`schedule_prune_analytics_daily_facts`** (registered in core worker schedules) for rollup materialization and one-year retention prune.
25
+
26
+ ## [2.2.0] - 2026-06-30
27
+
28
+ Transactional outbox (block, follow, restriction), post views retention, and incremental view-count aggregation. Hosts must update **`config.toml`**, run **`cf-alembic`**, and run the worker for mirror fanout and retention jobs.
29
+
30
+ ### Added
31
+
32
+ - **Post views retention:** Incremental **`view_count`** aggregation via **`post_stats.view_count_aggregated_up_to`** (high-water mark on **`post_views.created_at`**); daily worker **`schedule_prune_post_views`** (05:00 UTC) deletes aggregated rows older than **`retention.post_views_retention_days`** (default **90**). See **`docs/domains/posts/post_stats_aggregation.md`**.
33
+ - **Transactional outbox (block pilot):** `user.event_outbox`, worker poller/handler for **`user.block.relationship_changed`**, durable post/comment/notification block mirror fanout. See **`docs/platform/event-outbox-design.md`**.
34
+ - **Transactional outbox (follow, Phase 2):** **`user.follow.relationship_changed`** enqueued with truth changes; worker reconciles **`post.user_followers_lookup`** (outbox-only mirror path, matching block). Non-retryable handler errors dead-letter immediately (decision **#22**).
35
+ - **Transactional outbox (restriction, Phase 3):** **`moderation.restriction.relationship_changed`** enqueued on truth changes and no-op healing; worker reconciles post/comment/user/notification restriction lookup mirrors (outbox-only, matching block/follow). Poller E2E in **`tests/integration/worker/moderation_outbox_test.py`**. Run **`cf-alembic`** for **`moderation_add_event_outbox`**.
36
+
37
+ ### Fixed
38
+
39
+ - **Appeal approval retry:** Same-decision retries on already-approved appeals re-run `clear_user_restriction` instead of returning **400**, recovering when restriction removal failed after the appeal was persisted.
40
+ - **Worker staging sweeps:** Avatar and banner stale-staging cron schedules call **`sweep_*_staging_housekeeping`** application entrypoints instead of domain dependencies directly.
41
+
42
+ ### Changed
43
+
44
+ - **Post stats aggregation:** **`view_count`** is maintained incrementally from **`post_views`** since the last high-water mark instead of a full **`count(*)`** per run; tombstone resets clear **`view_count_aggregated_up_to`**.
45
+
46
+ ### Breaking
47
+
48
+ - **Breaking (hosts — config):** When upgrading to a release that includes the outbox block pilot, update **`config.toml`** (and deploy templates) before startup:
49
+ - Rename **`[http-client]`** → **`[http_client]`** (field name on **`Settings`** is now **`http_client`** with no hyphen alias).
50
+ - Add **`[outbox]`** with **`min_connections`** and **`max_connections`** (defaults in core **`config.toml.template`**: **2** / **12**). The outbox pool is opened only by the **worker** process (and test workers via **`create_worker_settings_for_shared_runtime`**), not the API lifespan.
51
+ - Run **`cf-alembic`** so the **`user`** migration stream applies **`user_add_event_outbox`** (`"user".event_outbox`).
52
+ - Hosts that only run the API (no worker) still need **`[outbox]`** in config because **`Settings`** requires it; the API does not connect that pool.
53
+ - **Breaking (hosts — migration):** When upgrading to a release that includes restriction outbox (Phase 3), run **`cf-alembic`** so the **`moderation`** migration stream applies **`moderation_add_event_outbox`** (`moderation.event_outbox`). The worker must run **`schedule_poll_moderation_outbox`** (registered in core worker schedules) for durable restriction mirror fanout.
54
+ - **Breaking (hosts — config):** Add **`[retention]`** with **`post_views_retention_days`** (default **90**). Deploy variable **`RETENTION_POST_VIEWS_DAYS`**. Hosts that subclass **`Settings`** may extend **`RetentionConfig`** with additional retention keys (for example calculator views on Henry).
55
+ - **Breaking (hosts — migration):** When upgrading to a release that includes post views retention, run **`cf-alembic`** so the **`post`** migration stream applies **`post_post_views_retention_high_water_mark`** (`post_stats.view_count_aggregated_up_to`, **`post_views`** retention indexes, backfill). The worker must run **`schedule_prune_post_views`** (registered in core worker schedules) for age-based **`post_views`** pruning.
56
+
7
57
  ## [2.1.0] - 2026-06-25
8
58
 
9
59
  Dependency and tooling refresh. No API, schema, or host config changes — bump the package and redeploy; no **`cf-alembic`** step.
@@ -349,4 +399,5 @@ First **SemVer-stable** release per **`docs/library/package-api.md`**.
349
399
  [1.8.1]: https://github.com/NepNepFFXIV/core-framework/compare/v1.8.0...v1.8.1
350
400
  [2.0.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.8.1...v2.0.0
351
401
  [2.1.0]: https://github.com/NepNepFFXIV/core-framework/compare/v2.0.0...v2.1.0
352
- [unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v2.1.0...HEAD
402
+ [2.2.0]: https://github.com/NepNepFFXIV/core-framework/compare/v2.1.0...v2.2.0
403
+ [unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v2.2.0...HEAD
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: core-framework
3
- Version: 2.1.0
3
+ Version: 2.3.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
@@ -0,0 +1,74 @@
1
+ import asyncio
2
+ from logging.config import fileConfig
3
+
4
+ from sqlalchemy import pool, text
5
+ from sqlalchemy.engine import Connection
6
+ from sqlalchemy.ext.asyncio import async_engine_from_config
7
+
8
+ from alembic import context
9
+ from core_framework.core.settings import load_default_settings
10
+
11
+ settings = load_default_settings()
12
+
13
+ config = context.config
14
+
15
+ if config.config_file_name is not None:
16
+ fileConfig(config.config_file_name)
17
+
18
+ target_metadata = None
19
+
20
+ config.set_main_option("sqlalchemy.url", settings.write_postgres.alembic_postgres_url)
21
+
22
+
23
+ def run_migrations_offline() -> None:
24
+ """Run migrations in 'offline' mode."""
25
+ url = config.get_main_option("sqlalchemy.url")
26
+ context.configure(
27
+ url=url,
28
+ target_metadata=target_metadata,
29
+ literal_binds=True,
30
+ dialect_opts={"paramstyle": "named"},
31
+ )
32
+
33
+ quoted_schema_name = f'"{settings.postgres_schemas.schema_analytics.replace('"', '""')}"'
34
+
35
+ with context.begin_transaction():
36
+ context.execute(text(f"set search_path to {quoted_schema_name}"))
37
+ context.run_migrations()
38
+
39
+
40
+ def do_run_migrations(connection: Connection) -> None:
41
+ quoted_schema_name = f'"{settings.postgres_schemas.schema_analytics.replace('"', '""')}"'
42
+
43
+ connection.execute(text(f"create schema if not exists {quoted_schema_name}"))
44
+ connection.execute(text(f"set search_path to {quoted_schema_name}"))
45
+ connection.commit()
46
+
47
+ context.configure(connection=connection, target_metadata=target_metadata)
48
+
49
+ with context.begin_transaction():
50
+ context.run_migrations()
51
+
52
+
53
+ async def run_async_migrations() -> None:
54
+ """Run migrations in 'online' mode."""
55
+ connectable = async_engine_from_config(
56
+ config.get_section(config.config_ini_section, {}),
57
+ prefix="sqlalchemy.",
58
+ poolclass=pool.NullPool,
59
+ )
60
+
61
+ async with connectable.connect() as connection:
62
+ await connection.run_sync(do_run_migrations)
63
+
64
+ await connectable.dispose()
65
+
66
+
67
+ def run_migrations_online() -> None:
68
+ asyncio.run(run_async_migrations())
69
+
70
+
71
+ if context.is_offline_mode():
72
+ run_migrations_offline()
73
+ else:
74
+ run_migrations_online()
@@ -0,0 +1,33 @@
1
+ """analytics daily_fact
2
+
3
+ Revision ID: 5b9ac98bb150
4
+ Revises:
5
+ Create Date: 2026-07-01 12:00:00.000000
6
+
7
+ """
8
+
9
+ from collections.abc import Sequence
10
+
11
+ from alembic import op
12
+
13
+ revision: str = "5b9ac98bb150"
14
+ down_revision: str | Sequence[str] | None = None
15
+ branch_labels: str | Sequence[str] | None = None
16
+ depends_on: str | Sequence[str] | None = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ op.execute("""
21
+ create table daily_fact (
22
+ subject_type text not null,
23
+ activity_type text not null,
24
+ day date not null,
25
+ kind text not null default '',
26
+ count bigint not null default 0,
27
+ primary key (subject_type, activity_type, day, kind)
28
+ );
29
+ """)
30
+
31
+
32
+ def downgrade() -> None:
33
+ op.execute("drop table if exists daily_fact;")
@@ -0,0 +1,27 @@
1
+ """analytics daily_fact day index for retention prune
2
+
3
+ Revision ID: 7f2a9c1d4e8b
4
+ Revises: 5b9ac98bb150
5
+ Create Date: 2026-07-02 12:00:00.000000
6
+
7
+ """
8
+
9
+ from collections.abc import Sequence
10
+
11
+ from alembic import op
12
+
13
+ revision: str = "7f2a9c1d4e8b"
14
+ down_revision: str | Sequence[str] | None = "5b9ac98bb150"
15
+ branch_labels: str | Sequence[str] | None = None
16
+ depends_on: str | Sequence[str] | None = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ op.execute("""
21
+ create index if not exists idx_daily_fact_day
22
+ on daily_fact (day);
23
+ """)
24
+
25
+
26
+ def downgrade() -> None:
27
+ op.execute("drop index if exists idx_daily_fact_day;")
@@ -0,0 +1,52 @@
1
+ """add event_outbox table for transactional outbox
2
+
3
+ Revision ID: 3a8e61ac8ae2
4
+ Revises: v1_mod_init
5
+ Create Date: 2026-06-27 12:00:00.000000
6
+
7
+ """
8
+
9
+ from collections.abc import Sequence
10
+
11
+ from alembic import op
12
+
13
+ revision: str = "3a8e61ac8ae2"
14
+ down_revision: str | Sequence[str] | None = "v1_mod_init"
15
+ branch_labels: str | Sequence[str] | None = None
16
+ depends_on: str | Sequence[str] | None = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ op.execute("""
21
+ create table event_outbox (
22
+ id bigint generated always as identity primary key,
23
+ created_at timestamptz not null default now(),
24
+ event_name text not null,
25
+ payload jsonb not null,
26
+ payload_version smallint not null default 1,
27
+ idempotency_key text not null,
28
+ aggregate_type text not null,
29
+ aggregate_id text not null,
30
+ status text not null default 'pending',
31
+ available_at timestamptz not null default now(),
32
+ attempt_count integer not null default 0,
33
+ failure_count integer not null default 0,
34
+ locked_at timestamptz,
35
+ locked_by text,
36
+ last_error text,
37
+ metadata jsonb,
38
+ constraint event_outbox_idempotency_key_unique unique (idempotency_key),
39
+ constraint event_outbox_status_valid
40
+ check (status in ('pending', 'processing', 'done', 'dead'))
41
+ );
42
+ """)
43
+
44
+ op.execute("""
45
+ create index idx_event_outbox_pending_available
46
+ on event_outbox (available_at, id)
47
+ where status = 'pending';
48
+ """)
49
+
50
+
51
+ def downgrade() -> None:
52
+ op.execute("drop table if exists event_outbox;")
@@ -0,0 +1,34 @@
1
+ """add dead-row partial indexes on event_outbox for admin ops
2
+
3
+ Revision ID: de7c41fba3e8
4
+ Revises: 3a8e61ac8ae2
5
+ Create Date: 2026-06-28 12:00:00.000000
6
+
7
+ """
8
+
9
+ from collections.abc import Sequence
10
+
11
+ from alembic import op
12
+
13
+ revision: str = "de7c41fba3e8"
14
+ down_revision: str | Sequence[str] | None = "3a8e61ac8ae2"
15
+ branch_labels: str | Sequence[str] | None = None
16
+ depends_on: str | Sequence[str] | None = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ op.execute("""
21
+ create index idx_event_outbox_dead_created_at
22
+ on event_outbox (created_at desc, id)
23
+ where status = 'dead';
24
+ """)
25
+ op.execute("""
26
+ create index idx_event_outbox_dead_event_created
27
+ on event_outbox (event_name, created_at)
28
+ where status = 'dead';
29
+ """)
30
+
31
+
32
+ def downgrade() -> None:
33
+ op.execute("drop index if exists idx_event_outbox_dead_event_created;")
34
+ op.execute("drop index if exists idx_event_outbox_dead_created_at;")
@@ -0,0 +1,28 @@
1
+ """add pending created_at partial index on event_outbox for admin oldest-pending
2
+
3
+ Revision ID: cf840f06eff3
4
+ Revises: de7c41fba3e8
5
+ Create Date: 2026-06-29 12:00:00.000000
6
+
7
+ """
8
+
9
+ from collections.abc import Sequence
10
+
11
+ from alembic import op
12
+
13
+ revision: str = "cf840f06eff3"
14
+ down_revision: str | Sequence[str] | None = "de7c41fba3e8"
15
+ branch_labels: str | Sequence[str] | None = None
16
+ depends_on: str | Sequence[str] | None = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ op.execute("""
21
+ create index idx_event_outbox_pending_created_at
22
+ on event_outbox (created_at, id)
23
+ where status = 'pending';
24
+ """)
25
+
26
+
27
+ def downgrade() -> None:
28
+ op.execute("drop index if exists idx_event_outbox_pending_created_at;")