core-framework 1.0.0__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (390) hide show
  1. core_framework-1.2.0/.cursor/rules/api-layer.mdc +37 -0
  2. core_framework-1.2.0/.cursor/rules/constants-final.mdc +15 -0
  3. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/domain-input-guards.mdc +3 -0
  4. core_framework-1.2.0/.cursor/rules/domain-repository-exceptions.mdc +20 -0
  5. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/layer-boundaries.mdc +3 -2
  6. core_framework-1.2.0/.cursor/rules/no-code-in-docs.mdc +30 -0
  7. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/repository-read-consistency.mdc +7 -5
  8. core_framework-1.2.0/.cursor/rules/strong-read-opt-in.mdc +22 -0
  9. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/tech-stack.mdc +2 -0
  10. core_framework-1.2.0/.cursor/skills/add-domain/SKILL.md +203 -0
  11. {core_framework-1.0.0 → core_framework-1.2.0}/.github/workflows/_deploy.yml +3 -37
  12. {core_framework-1.0.0 → core_framework-1.2.0}/.github/workflows/dev-ci-cd.yaml +3 -1
  13. {core_framework-1.0.0 → core_framework-1.2.0}/.github/workflows/test.yaml +3 -1
  14. {core_framework-1.0.0 → core_framework-1.2.0}/.gitignore +4 -0
  15. {core_framework-1.0.0 → core_framework-1.2.0}/.pre-commit-config.yaml +1 -1
  16. core_framework-1.2.0/CHANGELOG.md +127 -0
  17. core_framework-1.2.0/LICENSE +21 -0
  18. core_framework-1.2.0/PKG-INFO +131 -0
  19. core_framework-1.2.0/README.md +97 -0
  20. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/comment/alembic/env.py +6 -1
  21. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/comment/alembic/versions/v1_comment_init_baseline.py +7 -1
  22. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/extension/alembic/env.py +6 -1
  23. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/moderation/alembic/env.py +6 -1
  24. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +34 -37
  25. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/notification/alembic/env.py +6 -1
  26. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/notification/alembic/versions/v1_notif_init_baseline.py +11 -4
  27. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/post/alembic/env.py +6 -1
  28. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/post/alembic/versions/v1_post_init_baseline.py +26 -11
  29. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/user/alembic/env.py +6 -1
  30. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/user/alembic/versions/v1_user_init_baseline.py +39 -0
  31. {core_framework-1.0.0 → core_framework-1.2.0}/config.toml +2 -2
  32. {core_framework-1.0.0 → core_framework-1.2.0}/config.toml.template +1 -1
  33. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/comments/router.py +17 -10
  34. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/moderation/router.py +31 -22
  35. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/users/router.py +12 -12
  36. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/comments/authenticated/router.py +26 -13
  37. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/comments/authenticated/schemas.py +1 -1
  38. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/comments/public/router.py +1 -1
  39. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/dependencies.py +1 -1
  40. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/events/router.py +5 -3
  41. core_framework-1.2.0/core_framework/api/notifications/authenticated/schemas.py +104 -0
  42. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/posts/authenticated/router.py +22 -8
  43. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/posts/public/router.py +5 -1
  44. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/system/router.py +11 -8
  45. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/users/authenticated/router.py +205 -9
  46. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/users/authenticated/schemas.py +7 -0
  47. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/auth/access_service.py +7 -6
  48. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/auth/auth_service.py +8 -3
  49. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/bootstrap.py +3 -3
  50. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/comments/admin_service.py +19 -9
  51. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/comments/authenticated_service.py +25 -12
  52. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/comments/public_service.py +14 -0
  53. core_framework-1.2.0/core_framework/application/events/event_service.py +72 -0
  54. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/events/event_token.py +6 -4
  55. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/moderation/appeal_service.py +9 -3
  56. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/moderation/user_service.py +14 -12
  57. core_framework-1.2.0/core_framework/application/notifications/README.md +16 -0
  58. core_framework-1.2.0/core_framework/application/notifications/enums.py +9 -0
  59. core_framework-1.2.0/core_framework/application/notifications/inbox_service.py +247 -0
  60. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/notifications/notification_service.py +27 -4
  61. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/posts/aggregation_service.py +9 -2
  62. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/posts/authenticated_service.py +6 -6
  63. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/posts/public_service.py +29 -2
  64. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/shared/enums.py +0 -5
  65. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/shared/user_agent.py +1 -1
  66. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/users/admin_service.py +19 -13
  67. core_framework-1.2.0/core_framework/application/users/aggregation_service.py +18 -0
  68. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/users/authenticated_service.py +83 -6
  69. core_framework-1.2.0/core_framework/application/users/public_service.py +8 -0
  70. core_framework-1.2.0/core_framework/asgi.py +4 -0
  71. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/bundled_alembic.py +4 -18
  72. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/__init__.py +3 -7
  73. core_framework-1.2.0/core_framework/core/cache.py +136 -0
  74. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/database.py +1 -4
  75. core_framework-1.2.0/core_framework/core/firebase.py +21 -0
  76. core_framework-1.2.0/core_framework/core/logging.py +83 -0
  77. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/observability.py +2 -7
  78. core_framework-1.2.0/core_framework/core/redis.py +57 -0
  79. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/runtime.py +2 -8
  80. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/settings.py +3 -2
  81. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/comment/README.md +3 -1
  82. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/comment/__init__.py +8 -1
  83. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/comment/exceptions.py +1 -1
  84. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/comment/models.py +17 -0
  85. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/comment/repository.py +46 -25
  86. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/comment/service.py +20 -18
  87. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/moderation/__init__.py +2 -0
  88. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/moderation/repository.py +66 -58
  89. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/moderation/service.py +8 -8
  90. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/notification/README.md +16 -15
  91. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/notification/enums.py +2 -0
  92. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/notification/models.py +1 -0
  93. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/notification/repository.py +5 -1
  94. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/post/README.md +15 -7
  95. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/post/enums.py +1 -0
  96. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/post/exceptions.py +1 -1
  97. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/post/models.py +8 -0
  98. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/post/repository.py +195 -42
  99. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/post/service.py +59 -16
  100. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/README.md +14 -0
  101. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/__init__.py +4 -0
  102. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/models.py +22 -0
  103. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/repository.py +209 -2
  104. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/service.py +87 -9
  105. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/utils.py +10 -0
  106. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/main.py +8 -14
  107. core_framework-1.2.0/core_framework/testing/__init__.py +7 -0
  108. core_framework-1.2.0/core_framework/testing/arq.py +80 -0
  109. {core_framework-1.0.0/tests/fixtures → core_framework-1.2.0/core_framework/testing}/auth.py +37 -43
  110. core_framework-1.2.0/core_framework/testing/config.py +28 -0
  111. core_framework-1.2.0/core_framework/testing/containers.py +63 -0
  112. core_framework-1.2.0/core_framework/testing/firebase.py +77 -0
  113. core_framework-1.2.0/core_framework/testing/hookspecs.py +14 -0
  114. core_framework-1.2.0/core_framework/testing/httpx_test_client.py +17 -0
  115. core_framework-1.2.0/core_framework/testing/migrations.py +38 -0
  116. core_framework-1.2.0/core_framework/testing/plugin.py +176 -0
  117. core_framework-1.2.0/core_framework/worker/main.py +73 -0
  118. core_framework-1.2.0/core_framework/worker/schedules/__init__.py +49 -0
  119. core_framework-1.2.0/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +39 -0
  120. core_framework-1.2.0/core_framework/worker/schedules/schedule_aggregate_post_stats.py +39 -0
  121. core_framework-1.2.0/core_framework/worker/schedules/schedule_aggregate_user_stats.py +39 -0
  122. core_framework-1.2.0/core_framework/worker/schedules/schedule_expired_account_deletions.py +29 -0
  123. core_framework-1.2.0/core_framework/worker/schedules/schedule_expired_mute_lifts.py +29 -0
  124. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/worker/tasks/__init__.py +2 -0
  125. core_framework-1.2.0/core_framework/worker/tasks/process_account_deletion.py +15 -0
  126. core_framework-1.2.0/core_framework/worker/tasks/process_aggregate_comment_stats.py +21 -0
  127. core_framework-1.2.0/core_framework/worker/tasks/process_aggregate_post_stats.py +18 -0
  128. core_framework-1.2.0/core_framework/worker/tasks/process_aggregate_user_stats.py +18 -0
  129. core_framework-1.2.0/core_framework/worker/tasks/process_mute_lift.py +15 -0
  130. core_framework-1.2.0/core_framework/worker/worker_context.py +20 -0
  131. {core_framework-1.0.0 → core_framework-1.2.0}/docker-compose.yml +1 -0
  132. {core_framework-1.0.0 → core_framework-1.2.0}/dockerfile +17 -16
  133. {core_framework-1.0.0 → core_framework-1.2.0}/docs/api.md +14 -4
  134. {core_framework-1.0.0 → core_framework-1.2.0}/docs/architecture-decisions.md +79 -0
  135. {core_framework-1.0.0 → core_framework-1.2.0}/docs/architecture.md +21 -7
  136. {core_framework-1.0.0 → core_framework-1.2.0}/docs/conventions.md +11 -10
  137. {core_framework-1.0.0 → core_framework-1.2.0}/docs/core-framework-migration.md +21 -12
  138. {core_framework-1.0.0 → core_framework-1.2.0}/docs/database-triggers.md +17 -45
  139. core_framework-1.2.0/docs/event-outbox-design.md +528 -0
  140. core_framework-1.2.0/docs/flows/comments/comment_stats_aggregation.md +69 -0
  141. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/comments/retrieve_comments.md +2 -1
  142. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/events/events.md +4 -3
  143. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/mentions/mentions_in_content.md +1 -1
  144. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/notifications/notification_inbox.md +46 -37
  145. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/posts/author_context.md +1 -0
  146. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/posts/hashtag_discovery.md +3 -0
  147. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/posts/post_like.md +1 -1
  148. core_framework-1.2.0/docs/flows/posts/post_stats_aggregation.md +81 -0
  149. core_framework-1.2.0/docs/flows/users/follow.md +215 -0
  150. core_framework-1.2.0/docs/flows/users/my_posts_and_comments.md +108 -0
  151. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/users/user_removal.md +2 -2
  152. core_framework-1.2.0/docs/follow-system-design.md +419 -0
  153. core_framework-1.2.0/docs/package-api.md +287 -0
  154. core_framework-1.2.0/docs/testing-plugin-design.md +391 -0
  155. {core_framework-1.0.0 → core_framework-1.2.0}/makefile +2 -7
  156. {core_framework-1.0.0 → core_framework-1.2.0}/pyproject.toml +28 -8
  157. core_framework-1.2.0/tests/conftest.py +25 -0
  158. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/_http_helpers.py +1 -1
  159. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/admin/posts/router_test.py +1 -1
  160. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/admin/users/router_test.py +30 -171
  161. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/auth/router_test.py +1 -1
  162. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +1 -1
  163. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/comments/public/router_test.py +75 -0
  164. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/events/router_test.py +12 -21
  165. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/notifications/router_test.py +79 -22
  166. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/posts/authenticated/post_writes_integration_test.py +1 -1
  167. core_framework-1.2.0/tests/integration/api/posts/followers_visibility_test.py +123 -0
  168. core_framework-1.2.0/tests/integration/api/posts/post_stats_dirty_marking_test.py +219 -0
  169. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/posts/public/router_test.py +64 -0
  170. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/system/router_test.py +8 -4
  171. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/users/authenticated/router_test.py +79 -3
  172. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/users/public/router_test.py +1 -1
  173. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/worker/account_deletion_test.py +66 -91
  174. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/worker/aggregate_comment_stats_test.py +78 -70
  175. core_framework-1.2.0/tests/integration/worker/aggregate_post_stats_test.py +267 -0
  176. core_framework-1.2.0/tests/integration/worker/aggregate_user_stats_test.py +144 -0
  177. core_framework-1.2.0/tests/integration/worker/conftest.py +21 -0
  178. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/worker/mute_lift_test.py +47 -32
  179. core_framework-1.2.0/tests/integration/worker/utils_test.py +33 -0
  180. {core_framework-1.0.0 → core_framework-1.2.0}/tests/unit/application/events/event_service_test.py +3 -3
  181. {core_framework-1.0.0 → core_framework-1.2.0}/tests/unit/application/notifications/inbox_service_test.py +8 -8
  182. {core_framework-1.0.0 → core_framework-1.2.0}/tests/unit/domains/user/service_test.py +5 -5
  183. {core_framework-1.0.0 → core_framework-1.2.0}/uv.lock +299 -254
  184. core_framework-1.0.0/.cursor/rules/api-layer.mdc +0 -27
  185. core_framework-1.0.0/CHANGELOG.md +0 -119
  186. core_framework-1.0.0/PKG-INFO +0 -22
  187. core_framework-1.0.0/README.md +0 -36
  188. core_framework-1.0.0/core_framework/api/notifications/authenticated/schemas.py +0 -51
  189. core_framework-1.0.0/core_framework/application/events/event_service.py +0 -51
  190. core_framework-1.0.0/core_framework/application/notifications/inbox_service.py +0 -107
  191. core_framework-1.0.0/core_framework/application/users/public_service.py +0 -7
  192. core_framework-1.0.0/core_framework/core/cache.py +0 -234
  193. core_framework-1.0.0/core_framework/core/logging.py +0 -99
  194. core_framework-1.0.0/core_framework/core/redis.py +0 -135
  195. core_framework-1.0.0/core_framework/worker/main.py +0 -56
  196. core_framework-1.0.0/core_framework/worker/schedules/__init__.py +0 -35
  197. core_framework-1.0.0/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +0 -32
  198. core_framework-1.0.0/core_framework/worker/schedules/schedule_aggregate_post_view_counts.py +0 -28
  199. core_framework-1.0.0/core_framework/worker/schedules/schedule_expired_account_deletions.py +0 -24
  200. core_framework-1.0.0/core_framework/worker/schedules/schedule_expired_mute_lifts.py +0 -24
  201. core_framework-1.0.0/core_framework/worker/tasks/process_account_deletion.py +0 -13
  202. core_framework-1.0.0/core_framework/worker/tasks/process_aggregate_comment_stats.py +0 -19
  203. core_framework-1.0.0/core_framework/worker/tasks/process_aggregate_post_stats.py +0 -12
  204. core_framework-1.0.0/core_framework/worker/tasks/process_mute_lift.py +0 -13
  205. core_framework-1.0.0/dockerfile.worker +0 -43
  206. core_framework-1.0.0/docs/flows/comments/comment_stats_aggregation.md +0 -134
  207. core_framework-1.0.0/docs/flows/posts/post_stats_aggregation.md +0 -63
  208. core_framework-1.0.0/docs/package-api.md +0 -149
  209. core_framework-1.0.0/tests/conftest.py +0 -269
  210. core_framework-1.0.0/tests/integration/worker/aggregate_post_view_test.py +0 -205
  211. core_framework-1.0.0/tests/integration/worker/conftest.py +0 -25
  212. core_framework-1.0.0/tests/integration/worker/utils_test.py +0 -54
  213. core_framework-1.0.0/tests/unit/core/cache_test.py +0 -692
  214. core_framework-1.0.0/tests/unit/domains/comment/__init__.py +0 -0
  215. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/api-reference-docs.mdc +0 -0
  216. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/api-security.mdc +0 -0
  217. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/api-validation.mdc +0 -0
  218. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/application-layer.mdc +0 -0
  219. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/database-triggers.md +0 -0
  220. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/domain-caller-context.mdc +0 -0
  221. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/domain-imports.mdc +0 -0
  222. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/flow-documentation.mdc +0 -0
  223. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/implementation-workflow.mdc +0 -0
  224. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/integration-test-strategy.mdc +0 -0
  225. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/no-docstrings.mdc +0 -0
  226. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/rules/postgres-config-conventions.mdc +0 -0
  227. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/skills/add-config/SKILL.md +0 -0
  228. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/skills/code-review/SKILL.md +0 -0
  229. {core_framework-1.0.0 → core_framework-1.2.0}/.cursor/skills/recommend-features/SKILL.md +0 -0
  230. {core_framework-1.0.0 → core_framework-1.2.0}/.dockerignore +0 -0
  231. {core_framework-1.0.0 → core_framework-1.2.0}/.github/workflows/manual-deployment.yaml +0 -0
  232. {core_framework-1.0.0 → core_framework-1.2.0}/.logfire/.gitignore +0 -0
  233. {core_framework-1.0.0 → core_framework-1.2.0}/.logfire/logfire_credentials.json +0 -0
  234. {core_framework-1.0.0 → core_framework-1.2.0}/.python-version +0 -0
  235. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/comment/alembic/README +0 -0
  236. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/comment/alembic/script.py.mako +0 -0
  237. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/comment/alembic.ini +0 -0
  238. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/extension/alembic/README +0 -0
  239. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/extension/alembic/script.py.mako +0 -0
  240. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/extension/alembic/versions/v1_ext_init_baseline.py +0 -0
  241. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/extension/alembic.ini +0 -0
  242. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/moderation/alembic/README +0 -0
  243. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/moderation/alembic/script.py.mako +0 -0
  244. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/moderation/alembic.ini +0 -0
  245. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/notification/alembic/README +0 -0
  246. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/notification/alembic/script.py.mako +0 -0
  247. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/notification/alembic.ini +0 -0
  248. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/post/alembic/README +0 -0
  249. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/post/alembic/script.py.mako +0 -0
  250. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/post/alembic.ini +0 -0
  251. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/user/alembic/README +0 -0
  252. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/user/alembic/script.py.mako +0 -0
  253. {core_framework-1.0.0 → core_framework-1.2.0}/alembic/user/alembic.ini +0 -0
  254. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/__init__.py +0 -0
  255. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/__init__.py +0 -0
  256. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/__init__.py +0 -0
  257. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/comments/schemas.py +0 -0
  258. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/moderation/__init__.py +0 -0
  259. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/moderation/schemas.py +0 -0
  260. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/posts/router.py +0 -0
  261. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/posts/schemas.py +0 -0
  262. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/router.py +0 -0
  263. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/users/__init__.py +0 -0
  264. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/admin/users/schemas.py +0 -0
  265. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/auth/__init__.py +0 -0
  266. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/auth/router.py +0 -0
  267. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/auth/schemas.py +0 -0
  268. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/comments/public/schemas.py +0 -0
  269. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/comments/router.py +0 -0
  270. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/comments/schemas.py +0 -0
  271. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/constants.py +0 -0
  272. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/events/schemas.py +0 -0
  273. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/notifications/authenticated/router.py +0 -0
  274. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/notifications/router.py +0 -0
  275. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/posts/authenticated/schemas.py +0 -0
  276. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/posts/public/schemas.py +0 -0
  277. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/posts/router.py +0 -0
  278. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/posts/schemas.py +0 -0
  279. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/router.py +0 -0
  280. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/schemas.py +0 -0
  281. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/system/__init__.py +0 -0
  282. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/users/__init__.py +0 -0
  283. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/users/authenticated/__init__.py +0 -0
  284. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/users/public/__init__.py +0 -0
  285. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/users/public/router.py +0 -0
  286. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/users/public/schemas.py +0 -0
  287. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/users/router.py +0 -0
  288. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/api/users/shared/schemas.py +0 -0
  289. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/__init__.py +0 -0
  290. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/auth/__init__.py +0 -0
  291. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/auth/models.py +0 -0
  292. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/comments/aggregation_service.py +0 -0
  293. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/events/README.md +0 -0
  294. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/events/models.py +0 -0
  295. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/moderation/__init__.py +0 -0
  296. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/moderation/moderator_service.py +0 -0
  297. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/moderation/report_service.py +0 -0
  298. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/moderation/scheduled_service.py +0 -0
  299. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/notifications/mute_service.py +0 -0
  300. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/posts/admin_service.py +0 -0
  301. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/shared/__init__.py +0 -0
  302. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/shared/exceptions.py +0 -0
  303. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/users/__init__.py +0 -0
  304. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/application/users/scheduled_service.py +0 -0
  305. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/constants.py +0 -0
  306. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/context.py +0 -0
  307. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/exception_handlers/__init__.py +0 -0
  308. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/exception_handlers/comment.py +0 -0
  309. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/exception_handlers/common.py +0 -0
  310. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/exception_handlers/moderation.py +0 -0
  311. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/exception_handlers/notification.py +0 -0
  312. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/exception_handlers/post.py +0 -0
  313. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/exception_handlers/setup.py +0 -0
  314. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/exception_handlers/user.py +0 -0
  315. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/http_client.py +0 -0
  316. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/middleware.py +0 -0
  317. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/core/pagination.py +0 -0
  318. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/__init__.py +0 -0
  319. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/comment/constants.py +0 -0
  320. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/comment/dependencies.py +0 -0
  321. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/comment/enums.py +0 -0
  322. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/exceptions.py +0 -0
  323. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/moderation/README.md +0 -0
  324. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/moderation/dependencies.py +0 -0
  325. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/moderation/enums.py +0 -0
  326. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/moderation/exceptions.py +0 -0
  327. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/moderation/models.py +0 -0
  328. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/notification/__init__.py +0 -0
  329. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/notification/dependencies.py +0 -0
  330. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/notification/exceptions.py +0 -0
  331. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/notification/service.py +0 -0
  332. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/post/__init__.py +0 -0
  333. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/post/constants.py +0 -0
  334. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/post/dependencies.py +0 -0
  335. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/constants.py +0 -0
  336. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/dependencies.py +0 -0
  337. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/enums.py +0 -0
  338. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/exceptions.py +0 -0
  339. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/domains/user/utils.py +0 -0
  340. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/migrate_cli.py +0 -0
  341. {core_framework-1.0.0 → core_framework-1.2.0}/core_framework/worker/__init__.py +0 -0
  342. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/auth/access_control.md +0 -0
  343. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/auth/registration.md +0 -0
  344. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/comments/admin_comments.md +0 -0
  345. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/comments/comment_report.md +0 -0
  346. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/comments/create_comment.md +0 -0
  347. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/comments/delete_comment.md +0 -0
  348. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/comments/edit_comment.md +0 -0
  349. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/moderation/appeals.md +0 -0
  350. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/moderation/internal_notes.md +0 -0
  351. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/moderation/moderator_actions.md +0 -0
  352. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/moderation/reports.md +0 -0
  353. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/moderation/restrictions.md +0 -0
  354. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/posts/admin_posts.md +0 -0
  355. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/users/account.md +0 -0
  356. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/users/account_deletion.md +0 -0
  357. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/users/blocks.md +0 -0
  358. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/users/change_history.md +0 -0
  359. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/users/check_username_exists.md +0 -0
  360. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/users/preferences.md +0 -0
  361. {core_framework-1.0.0 → core_framework-1.2.0}/docs/flows/users/profile.md +0 -0
  362. {core_framework-1.0.0 → core_framework-1.2.0}/tests/__init__.py +0 -0
  363. {core_framework-1.0.0/tests/fixtures → core_framework-1.2.0/tests/integration}/__init__.py +0 -0
  364. {core_framework-1.0.0/tests/integration → core_framework-1.2.0/tests/integration/api}/__init__.py +0 -0
  365. {core_framework-1.0.0/tests/integration/api → core_framework-1.2.0/tests/integration/api/admin}/__init__.py +0 -0
  366. {core_framework-1.0.0/tests/integration/api/admin → core_framework-1.2.0/tests/integration/api/admin/comments}/__init__.py +0 -0
  367. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/admin/comments/router_test.py +0 -0
  368. {core_framework-1.0.0/tests/integration/api/admin/comments → core_framework-1.2.0/tests/integration/api/admin/moderation}/__init__.py +0 -0
  369. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/admin/moderation/router_test.py +0 -0
  370. {core_framework-1.0.0/tests/integration/api/admin/moderation → core_framework-1.2.0/tests/integration/api/admin/posts}/__init__.py +0 -0
  371. {core_framework-1.0.0/tests/integration/api/admin/posts → core_framework-1.2.0/tests/integration/api/admin/users}/__init__.py +0 -0
  372. {core_framework-1.0.0/tests/integration/api/admin/users → core_framework-1.2.0/tests/integration/api/auth}/__init__.py +0 -0
  373. {core_framework-1.0.0/tests/integration/api/auth → core_framework-1.2.0/tests/integration/api/comments}/__init__.py +0 -0
  374. {core_framework-1.0.0/tests/integration/api/comments → core_framework-1.2.0/tests/integration/api/comments/public}/__init__.py +0 -0
  375. {core_framework-1.0.0/tests/integration/api/comments/public → core_framework-1.2.0/tests/integration/api/notifications}/__init__.py +0 -0
  376. {core_framework-1.0.0/tests/integration/api/notifications → core_framework-1.2.0/tests/integration/api/posts}/__init__.py +0 -0
  377. {core_framework-1.0.0 → core_framework-1.2.0}/tests/integration/api/posts/comment_count_aggregation_test.py +0 -0
  378. {core_framework-1.0.0/tests/integration/api/posts → core_framework-1.2.0/tests/integration/api/posts/public}/__init__.py +0 -0
  379. {core_framework-1.0.0/tests/integration/api/posts/public → core_framework-1.2.0/tests/integration/api/system}/__init__.py +0 -0
  380. {core_framework-1.0.0/tests/integration/api/system → core_framework-1.2.0/tests/integration/api/users}/__init__.py +0 -0
  381. {core_framework-1.0.0/tests/integration/api/users → core_framework-1.2.0/tests/integration/api/users/authenticated}/__init__.py +0 -0
  382. {core_framework-1.0.0/tests/integration/api/users/authenticated → core_framework-1.2.0/tests/integration/api/users/public}/__init__.py +0 -0
  383. {core_framework-1.0.0/tests/integration/api/users/public → core_framework-1.2.0/tests/integration/worker}/__init__.py +0 -0
  384. {core_framework-1.0.0/tests/integration/worker → core_framework-1.2.0/tests/unit}/__init__.py +0 -0
  385. {core_framework-1.0.0/tests/unit → core_framework-1.2.0/tests/unit/application/comments}/__init__.py +0 -0
  386. {core_framework-1.0.0 → core_framework-1.2.0}/tests/unit/core/bundled_alembic_test.py +0 -0
  387. {core_framework-1.0.0 → core_framework-1.2.0}/tests/unit/core/migrate_cli_test.py +0 -0
  388. {core_framework-1.0.0 → core_framework-1.2.0}/tests/unit/core/pagination_test.py +0 -0
  389. {core_framework-1.0.0/tests/unit/application/comments → core_framework-1.2.0/tests/unit/domains}/__init__.py +0 -0
  390. {core_framework-1.0.0/tests/unit/domains → core_framework-1.2.0/tests/unit/domains/comment}/__init__.py +0 -0
@@ -0,0 +1,37 @@
1
+ ---
2
+ description: API layer should contain HTTP adapters only
3
+ globs:
4
+ - core_framework/api/**/*.py
5
+ ---
6
+
7
+ # API Layer Conventions
8
+
9
+ The API layer is an HTTP adapter, not a business-logic layer.
10
+
11
+ ## Scope
12
+
13
+ - Keep API files focused on routers, schemas, and HTTP dependencies.
14
+ - API code may call application services in `core_framework/application/...`.
15
+ - API code must not contain orchestration shared with workers.
16
+
17
+ ## Router Responsibilities
18
+
19
+ - Routers own HTTP concerns: status codes, `HTTPException`, `response_model`, request parsing.
20
+ - When a route sets **`response_model`**, annotate the handler return type as **`typing.Any`** (OpenAPI documents the real shape; the handler may return dicts or model-adjacent values before serialization). Routes with no response body (for example **`204`** and no **`response_model`**) continue to use **`None`**.
21
+ - Routers call `validate_*` schema helpers before invoking application services.
22
+ - Routers should remain thin and delegate business decisions to application/domain layers.
23
+
24
+ ## Route decorators (OpenAPI)
25
+
26
+ - Do **not** set **`summary=`** or **`description=`** on **`@router.get`**, **`post`**, **`patch`**, **`put`**, **`delete`**, or **`APIRoute`-style** registrations. Rely on paths, handler names, **`response_model`**, and Pydantic schemas. Do **not** add route **docstrings** for the same purpose either (see **`no-docstrings`**).
27
+
28
+ ## Response Models
29
+
30
+ - **Do not** use bare collection types (e.g. `list[Item]`) as `response_model`. Wrap them in a named response `BaseModel` with an **`items`** field (e.g. `class FooListResponse(BaseModel): items: list[FooItem]`).
31
+ - This keeps responses extensible (pagination, counts, metadata) without breaking the contract.
32
+
33
+ ## What to Avoid
34
+
35
+ - Do NOT implement reusable business workflows in API modules.
36
+ - Do NOT import domain internals directly; follow domain public API and dependencies rules.
37
+ - Do NOT place worker-only logic (scheduling/task orchestration) in API modules.
@@ -0,0 +1,15 @@
1
+ ---
2
+ description: Prefer typing.Final for module-level constants (not reassigned)
3
+ globs: core_framework/**/*.py
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Module-level constants and `Final`
8
+
9
+ When introducing or editing **module-level constants** (values that are fixed for the lifetime of the module and not meant to be reassigned):
10
+
11
+ - Annotate them with **`typing.Final`** and an explicit type, e.g. **`NAME: Final[int] = 42`** or **`_INTERNAL: Final[str] = "henry"`**.
12
+ - Prefer **`FrozenSet`**, **`frozenset` literals**, **`MappingProxyType`**, or immutable collections for grouped constants when mutation must be prevented — e.g. **`USER_PREFERENCE_UPDATE_COLUMNS`** in **`core_framework/domains/user/constants.py`**.
13
+ - **`Final`** is for **assignment** semantics (no rebinding): it does not freeze mutable object contents unless the type/container is immutable.
14
+
15
+ Do not refactor unrelated legacy constants only to add **`Final`** unless you are already touching that symbol.
@@ -31,6 +31,8 @@ Use **layered** checks: user contracts at the **API**, business preconditions at
31
31
 
32
32
  ### Shared string / pagination helpers (`core_framework.domains.utils`)
33
33
 
34
+ **Import boundary:** `domains/utils.py` may import **`MAX_PAGE_FETCH_SIZE`** from **`core_framework.core.pagination`** only. That module is shared pagination constants, not Postgres/Redis/HTTP infrastructure; it is not subject to the “core imports only in `dependencies.py`” rule for DB/cache clients.
35
+
34
36
  Use these across domains so validation stays consistent and call sites stay readable:
35
37
 
36
38
  | Helper | Use when |
@@ -39,6 +41,7 @@ Use these across domains so validation stays consistent and call sites stay read
39
41
  | **`require_non_blank_kwargs(a_id=..., b_id=...)`** | Several required strings at once; each **keyword name** is the field label in `DomainValidationError` (e.g. `author_id=author_id, content=content`). |
40
42
  | **`require_non_blank_if_not_none(viewer_id=..., ...)`** | Optional `str \| None` arguments: validate only when not `None` (still rejects `""`). |
41
43
  | **`validate_pagination_limit(limit=...)`** | Paginated reads: `limit` in `[1, MAX_PAGE_FETCH_SIZE]` (aligned with `core_framework.core.pagination`). |
44
+ | **`validate_pagination_offset(offset=...)`** | Paginated reads: `offset` must be **≥ 0** (aligned with `OffsetQueryParams`). |
42
45
  | **`require_positive_int(value=..., field=...)`** | **Integer** surrogate keys only (e.g. moderation `report_id`, `appeal_id`, `note_id`). Must be **≥ 1**. Domains that identify entities with **string** IDs (ULIDs) use **`require_non_blank_*`** instead—do not add this helper there. |
43
46
  | **`require_validated_update_has_allowed_fields(validated_update_request=..., allowed_fields=...)`** | PATCH-style dict updates: reject empty bodies and bodies with **no** recognized keys (use domain constants such as **`USER_*_UPDATE_FIELDS`**). |
44
47
 
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: When domain repositories vs services raise domain exceptions (thin façade)
3
+ globs:
4
+ - core_framework/domains/**/*.py
5
+ ---
6
+
7
+ # Domain: repository exceptions vs service
8
+
9
+ ## Thin service + single persistence step
10
+
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).
12
+
13
+ The service stays a façade: validate arguments, then **`await`** the repository method and let **domain exceptions** propagate.
14
+
15
+ ## Prefer service-side translation when
16
+
17
+ - The service **composes multiple** repository calls, **branches** on outcomes, or maps the same SQL outcome to **different domain errors**.
18
+ - Keeping the repository purely “data shaped” (**`Optional`** / **`bool`**) materially improves reuse or clarity for **multiple** service entrypoints calling the same repository method.
19
+
20
+ Existing domains may mix both styles; use this guide for **new work** and when **refactoring** a call site for clarity.
@@ -19,7 +19,8 @@ Use this architecture split consistently:
19
19
  ## Rules
20
20
 
21
21
  - API layer must not contain business orchestration that is shared with workers.
22
- - Worker layer must not contain domain business rules; call application services.
22
+ - Worker layer must not contain domain business rules; call application services only.
23
+ - Worker code must not import **`core_framework.domains`** packages (no direct domain repository/service usage from workers).
23
24
  - Application layer must not import FastAPI/Starlette HTTP types.
24
25
  - Domain layer must not import API, worker, or application modules.
25
26
  - Domain layer must not import global infrastructure except in `dependencies.py`.
@@ -29,7 +30,7 @@ Use this architecture split consistently:
29
30
  Dependencies point inward:
30
31
 
31
32
  - API -> application and/or domains
32
- - worker -> application and/or domains
33
+ - worker -> application
33
34
  - application -> domains
34
35
 
35
36
  Never the reverse.
@@ -0,0 +1,30 @@
1
+ ---
2
+ description: Docs avoid implementation code; HTTP interface listings and schema DDL are OK
3
+ globs: docs/**/*.md
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # No Code in Documentation
8
+
9
+ Documentation under **`docs/`** must not carry **implementation** snippets that belong in the repo (executable Python, shell, operational SQL queries, etc.). **`docs/`** describes **what** and **why**; the codebase shows **how**.
10
+
11
+ ## What to exclude
12
+
13
+ - Python class/function definitions or examples
14
+ - **SQL for data access or mutation** — `SELECT`, `INSERT`, `UPDATE`, `DELETE`, `MERGE`, CTE-heavy reports, cleanup queries, etc. (describe behavior in prose or name the operation; do not paste DML/query text)
15
+ - Shell commands
16
+ - Pseudocode that mimics real code syntax
17
+ - Inline backticked references to internal function/method/class names when prose suffices (prefer describing behavior)
18
+
19
+ ## What is allowed
20
+
21
+ - **HTTP API interface listings** — fenced **`http`** blocks or plain lines listing `METHOD /path # short note`. That is **surface contract**, not implementation code.
22
+ - **SQL for table creation / physical design** — e.g. `CREATE TABLE` (and table-level DDL in the same design story, such as `CREATE INDEX`). DDL is a design artifact, not runtime query text.
23
+ - ASCII diagrams and flowcharts (box-drawing characters)
24
+ - Markdown tables of fields and types (`jsonb`, `varchar(26)`, etc.)
25
+ - Plain-text lifecycle diagrams (arrows, boxes)
26
+ - File/directory tree layouts (plain text or a neutral fence such as `text` if helpful)
27
+
28
+ ## Rationale
29
+
30
+ Implementation snippets in docs drift from source. **HTTP listings** stay stable as the public contract. **DDL** documents intended physical shape. **DML** and **application code** belong in migrations and modules.
@@ -5,10 +5,12 @@ alwaysApply: true
5
5
 
6
6
  # Repository Read Consistency
7
7
 
8
- When a repository supports both replica reads and strong reads, choose consistency at the use-case level.
8
+ **Policy** (see **`strong-read-opt-in`**): use **strong** reads for **read-after-write** refetches in the same request or job; treat **other** strong-read additions as **opt-in** unless the user asked for them in that task. Repositories may still take **`strong_read_database`** on **`__init__`** for wiring symmetry while it remains unused.
9
+
10
+ When a repository supports both replica and strong reads, choose consistency at the use-case level.
9
11
 
10
12
  - Keep normal reads explicit (for example, `select_appeal`).
11
- - Add separate strong-read methods for read-after-write paths (for example, `select_appeal_strong`).
13
+ - Add separate strong-read methods only where needed for those flows (for example, `select_appeal_strong`).
12
14
  - Services decide which method to call based on consistency needs.
13
15
 
14
16
  ## Method Structure
@@ -29,9 +31,9 @@ When a repository supports both replica reads and strong reads, choose consisten
29
31
 
30
32
  ## Consistency Selection
31
33
 
32
- - Use strong reads for read-after-write behavior in the same request flow.
33
- - Use strong reads for decision-critical guards where stale data can cause incorrect branching.
34
- - Use normal reads for list/browse/history endpoints unless strict freshness is required.
34
+ - **Read-after-write** in the same request or worker job → **strong** read (required).
35
+ - **Decision-critical guards** where stale data causes wrong branching → strong reads only when the task adds that surface (**opt-in** unless the flow is read-after-write).
36
+ - **List/browse/history** normal reads unless strict freshness is explicitly required (**opt-in**).
35
37
  - Keep consistency decisions in services/use-case orchestration, not hidden in unrelated call paths.
36
38
 
37
39
  ```python
@@ -0,0 +1,22 @@
1
+ ---
2
+ description: Strong reads after writes are required; other strong-read surface is opt-in
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Strong reads (core-framework)
7
+
8
+ When editing **`core_framework/**/*.py`** in this repository:
9
+
10
+ ## Read-after-write (required)
11
+
12
+ After a **write** in the **same** HTTP request or worker job flow, any **refetch** of that persisted state must use a **strong** read (`strong_read_database`, **`*_strong`** repository methods, or service accessors that call them) so callers do not hit stale replica data. 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.
13
+
14
+ ## Other uses (opt-in)
15
+
16
+ Do **not** add **`strong_read_database`** usage, new **`*_strong`** repository or service accessors, or strong refetches for reasons **other** than read-after-write (for example list/browse freshness, or decision guards unrelated to an immediate prior write in the same flow) unless the **user explicitly requested** strong reads in the same task or message.
17
+
18
+ ## Wiring
19
+
20
+ Repositories should still accept **`strong_read_database: Postgres`** on **`__init__`** where that matches existing dependency wiring **even when no method reads from it yet**; keep **`self.strong_read_database`** unused until a caller needs strong reads.
21
+
22
+ Existing `*_strong` APIs and call sites stay as-is unless a task explicitly changes them.
@@ -9,4 +9,6 @@ alwaysApply: true
9
9
  - **Exception syntax**: In this codebase, Python 3.14 comma-style multi-exception handling (for example, `except A, B:`) is valid and intentional.
10
10
  - **Pydantic v2**: Use only Pydantic v2 APIs. No v1 patterns (e.g. use `model_dump` not `dict()`, `model_validate` not `parse_obj`, `model_config` not nested `Config` class, `field_validator` not `validator`).
11
11
  - **Prefer newer APIs**: When multiple APIs exist for the same purpose, prefer the newer, recommended one (e.g. `asyncio.TaskGroup` over `asyncio.gather` for concurrent async operations).
12
+ - **Imports**: Use **absolute** imports only (`from core_framework....`). Do not use relative imports (`from .module import ...`, `from ..parent import ...`). Applies across application, domains, API, core, workers, and tests (including **`__init__.py`** re-exports).
13
+ - **orjson over stdlib json**: Use `orjson` for all JSON serialization and deserialization. Do not use `import json` from the standard library.
12
14
  - **PostgreSQL 18**: SQL and migrations must be compatible with PostgreSQL 18. Use supported syntax and avoid deprecated features.
@@ -0,0 +1,203 @@
1
+ ---
2
+ name: add-domain
3
+ description: Scaffold a new domain in core-framework (settings, empty Alembic tree, domain package + bootstrap). Use when adding a brand-new domain under `core_framework/domains/<name>/` with a dedicated Postgres schema name and `alembic/<name>/` layout, when the user asks to "add a domain", "create a domain", or "bootstrap <name> domain".
4
+ ---
5
+
6
+ # Adding a New Domain (with its own DB schema)
7
+
8
+ This skill covers **scaffolding only**: settings, a full **Alembic directory layout** with an **empty** `versions/` folder (no `.py` migrations yet), domain package shells + bootstrap wiring.
9
+
10
+ **Do not** add `<domain>` to `ALEMBIC_DOMAINS` until at least one revision exists under `versions/` — otherwise `cf-alembic` would run `upgrade head` for an empty branch and fail.
11
+
12
+ It does **not** cover the API layer or tests — handle those separately following the existing rules.
13
+
14
+ Follow `implementation-workflow` (`/.cursor/rules/implementation-workflow.mdc`): one step per response, wait for review.
15
+
16
+ ## Inputs
17
+
18
+ - `<domain>` — snake_case singular name (e.g. `billing`, `audit`, `feed`).
19
+ - Postgres schema name — usually `<domain>` (no quoting needed unless reserved like `user`).
20
+
21
+ Reserve the schema name in config so code and future migrations agree; the **physical schema in Postgres** does not exist until a migration creates it.
22
+
23
+ ## Order
24
+
25
+ 1. Schema config wiring
26
+ 1. Alembic tree (all support files; **`versions/` empty**)
27
+ 1. Domain skeleton (files, **class shells only**, dependencies + bootstrap wiring — **no example methods, no SQL/queries**)
28
+ 1. Application bootstrap wiring
29
+ 1. Domain README and changelog
30
+
31
+ Stop after each step and wait for review.
32
+
33
+ ## 1. Schema config wiring
34
+
35
+ Per `postgres-config-conventions` (`/.cursor/rules/postgres-config-conventions.mdc`).
36
+
37
+ `core_framework/core/settings.py`:
38
+
39
+ ```text
40
+ class PostgresSchemasConfig(BaseModel):
41
+ schema_extension: str
42
+ schema_user: str
43
+ schema_moderation: str
44
+ schema_post: str
45
+ schema_comment: str
46
+ schema_notification: str
47
+ schema_<domain>: str # add
48
+ ```
49
+
50
+ `config.toml` and `config.toml.template` — add under `[postgres_schemas]`:
51
+
52
+ ```text
53
+ [postgres_schemas]
54
+ schema_<domain> = "<domain>"
55
+ ```
56
+
57
+ Do **not** add the schema name anywhere else. Schema lives only in `[postgres_schemas]`.
58
+
59
+ ## 2. Alembic tree (skeleton, empty `versions/`)
60
+
61
+ Create `alembic/<domain>/` by copying layout from an existing domain (e.g. `alembic/comment/`): **`alembic.ini`**, `alembic/env.py`, `alembic/script.py.mako`, `alembic/README`, and directory **`alembic/versions/`**.
62
+
63
+ - **`env.py`**: keep the same pattern as siblings (e.g. `config.set_main_option("sqlalchemy.url", settings.write_postgres.alembic_postgres_url)`).
64
+ - **`alembic/versions/`**: **must contain no `*.py` files** at scaffold time. Optional: add a **`.gitkeep`** so Git keeps the empty folder.
65
+
66
+ **Do not** append `<domain>` to `ALEMBIC_DOMAINS` in this step. The new tree is **not** part of `cf-alembic` until the follow-up adds the first revision **and** updates `ALEMBIC_DOMAINS` together.
67
+
68
+ ```
69
+ alembic/<domain>/
70
+ ├── alembic.ini
71
+ └── alembic/
72
+ ├── env.py
73
+ ├── README
74
+ ├── script.py.mako
75
+ └── versions/
76
+ └── .gitkeep # optional; no *.py here initially
77
+ ```
78
+
79
+ ## 3. Domain skeleton (shells + wiring only)
80
+
81
+ Create `core_framework/domains/<domain>/` with **stub types and wiring only**. Do **not** add example domain methods, repository queries, SQL strings, or business logic until a later change.
82
+
83
+ ```
84
+ core_framework/domains/<domain>/
85
+ ├── __init__.py
86
+ ├── README.md
87
+ ├── exceptions.py
88
+ ├── models.py # placeholder only (see below)
89
+ ├── repository.py
90
+ ├── service.py
91
+ ├── dependencies.py
92
+ ```
93
+
94
+ Skip `enums.py`, `constants.py`, and `utils.py` until needed.
95
+
96
+ ### `exceptions.py`
97
+
98
+ One base exception class only, e.g. `class Base<Domain>Exception(Exception): ...` with an empty body (or `pass`). No concrete exception subclasses yet unless required for imports.
99
+
100
+ ### `models.py`
101
+
102
+ No dataclasses or table-shaped models at scaffold time. Either an empty module or a single short comment that models will be added with the first real migration. **Do not** invent placeholder fields.
103
+
104
+ ### `repository.py`
105
+
106
+ A single `class <Domain>Repository:` whose **`__init__`** takes `(write_postgres, read_postgres, strong_read_postgres)` (or the project’s `Postgres` type) and assigns them to **private** instance attributes (e.g. `_write_database`, `_read_database`, `_strong_read_database`). **No other methods.**
107
+
108
+ ### `service.py`
109
+
110
+ A single `class <Domain>Service:` whose **`__init__`** takes `<Domain>Repository` and stores it on a **private** attribute (e.g. `_repository`). **No other methods.** No FastAPI/Starlette imports. No caller-context names per `domain-caller-context`.
111
+
112
+ When real behavior is added later, apply `repository-read-consistency` (`/.cursor/rules/repository-read-consistency.mdc`) for reads and `_strong` naming.
113
+
114
+ ### `__init__.py`
115
+
116
+ Export the **classes** other layers may import: base exception, `<Domain>Repository`, `<Domain>Service`. List them in `__all__` per `domain-imports`. Do **not** re-export module-level `*_repository` / `*_service` instances from `dependencies` here.
117
+
118
+ ### `dependencies.py`
119
+
120
+ Wire repository and service the same way as other domains (e.g. `domains/comment/dependencies.py`):
121
+
122
+ ```text
123
+ from core_framework.core.runtime import CoreRuntime, unconfigured_dependency
124
+ from core_framework.domains.<domain>.repository import <Domain>Repository
125
+ from core_framework.domains.<domain>.service import <Domain>Service
126
+
127
+
128
+ def build_<domain>_repository(runtime: CoreRuntime) -> <Domain>Repository:
129
+ return <Domain>Repository(
130
+ runtime.write_postgres,
131
+ runtime.read_postgres,
132
+ runtime.write_postgres, # strong placeholder until a dedicated handle exists
133
+ )
134
+
135
+
136
+ def build_<domain>_service(runtime: CoreRuntime) -> <Domain>Service:
137
+ return <Domain>Service(build_<domain>_repository(runtime))
138
+
139
+
140
+ def configure_<domain>_dependencies(runtime: CoreRuntime) -> None:
141
+ global <domain>_repository, <domain>_service
142
+ <domain>_repository = build_<domain>_repository(runtime)
143
+ <domain>_service = build_<domain>_service(runtime)
144
+
145
+
146
+ <domain>_repository = unconfigured_dependency("<Domain>Repository")
147
+ <domain>_service = unconfigured_dependency("<Domain>Service")
148
+
149
+ __all__ = [
150
+ "build_<domain>_repository",
151
+ "build_<domain>_service",
152
+ "configure_<domain>_dependencies",
153
+ "<domain>_repository",
154
+ "<domain>_service",
155
+ ]
156
+ ```
157
+
158
+ ## 4. Application bootstrap wiring
159
+
160
+ `core_framework/application/bootstrap.py` — add the configure call:
161
+
162
+ ```text
163
+ from core_framework.domains.<domain>.dependencies import configure_<domain>_dependencies
164
+
165
+ def configure_application_dependencies(*, runtime: CoreRuntime) -> None:
166
+ ...
167
+ configure_<domain>_dependencies(runtime)
168
+ ```
169
+
170
+ Worker `startup` and API `init_app` already call `configure_application_dependencies(runtime=...)`, so both processes pick up the new domain automatically.
171
+
172
+ ## 5. Docs
173
+
174
+ - `core_framework/domains/<domain>/README.md` — domain exists in code; `alembic/<domain>/` exists with **empty** `versions/`; **not** in `ALEMBIC_DOMAINS` until first revision; **no** DB schema object until a migration creates it.
175
+ - `CHANGELOG.md` — `### Added` under `[Unreleased]` for domain package, `schema_<domain>`, and Alembic skeleton path (optional). **Do not** list `ALEMBIC_DOMAINS` or revision ids until the first migration lands.
176
+ - Update `docs/architecture.md` and `docs/package-api.md` only when the domain exposes anything to the public surface.
177
+
178
+ ## Follow-up: first revision + enroll in `cf-alembic`
179
+
180
+ Do this in a **separate step/PR** when you have DDL to apply.
181
+
182
+ 1. Add **`alembic/<domain>/alembic/versions/*.py`** (at least one revision); `upgrade()` may create schema/tables/enums as designed.
183
+ 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).
184
+ 1. `make alembic` and deploy’s `cf-alembic` step pick up the new domain automatically — no makefile or `_deploy.yml` edits needed.
185
+
186
+ `downgrade()` policy for baselines in this repo: raise `RuntimeError` when downgrades are unsupported, per existing `v1_*_init` migrations.
187
+
188
+ ## What you do NOT need to touch
189
+
190
+ - `makefile` `alembic` target — uses `cf-alembic`.
191
+ - `.github/workflows/_deploy.yml` migration step — uses `cf-alembic`.
192
+ - `pyproject.toml` `[tool.hatch.build.targets.wheel.force-include] "alembic"` — already directory-level, ships new trees automatically.
193
+
194
+ ## Drift safety
195
+
196
+ `tests/unit/core/bundled_alembic_test.py` exercises `iter_alembic_ini_paths()` over **only** `ALEMBIC_DOMAINS`. A scaffolded domain with an empty `versions/` folder is **invisible** to that test until it is appended — which must happen **together** with the first revision.
197
+
198
+ ## Reminders
199
+
200
+ - One step per response, wait for review (`implementation-workflow`).
201
+ - Domain imports follow `domain-imports`; do not import API/application from domain.
202
+ - Never name domain symbols by caller (`Admin*`, `Api*`, `Worker*`) per `domain-caller-context`.
203
+ - When adding real repository methods later, follow `repository-read-consistency` (`_strong` naming where applicable).
@@ -120,47 +120,12 @@ jobs:
120
120
  FIREBASE_CONFIG_PATH="$FIREBASE_CONFIG_PATH" \
121
121
  docker compose -f "$COMPOSE_FILE" pull
122
122
 
123
- echo "Running database migrations (extension)"
123
+ echo "Running database migrations (cf-alembic iterates core_framework.bundled_alembic.ALEMBIC_DOMAINS in order)"
124
124
  docker run --rm \
125
125
  --network host \
126
126
  -v "$CONFIG_PATH:/app/config.toml:ro" \
127
127
  "$DOCKERHUB_USERNAME/core-framework:latest" \
128
- alembic -c /app/alembic/extension/alembic.ini upgrade head
129
-
130
- echo "Running database migrations (user)"
131
- docker run --rm \
132
- --network host \
133
- -v "$CONFIG_PATH:/app/config.toml:ro" \
134
- "$DOCKERHUB_USERNAME/core-framework:latest" \
135
- alembic -c /app/alembic/user/alembic.ini upgrade head
136
-
137
- echo "Running database migrations (moderation)"
138
- docker run --rm \
139
- --network host \
140
- -v "$CONFIG_PATH:/app/config.toml:ro" \
141
- "$DOCKERHUB_USERNAME/core-framework:latest" \
142
- alembic -c /app/alembic/moderation/alembic.ini upgrade head
143
-
144
- echo "Running database migrations (post)"
145
- docker run --rm \
146
- --network host \
147
- -v "$CONFIG_PATH:/app/config.toml:ro" \
148
- "$DOCKERHUB_USERNAME/core-framework:latest" \
149
- alembic -c /app/alembic/post/alembic.ini upgrade head
150
-
151
- echo "Running database migrations (comment)"
152
- docker run --rm \
153
- --network host \
154
- -v "$CONFIG_PATH:/app/config.toml:ro" \
155
- "$DOCKERHUB_USERNAME/core-framework:latest" \
156
- alembic -c /app/alembic/comment/alembic.ini upgrade head
157
-
158
- echo "Running database migrations (notification)"
159
- docker run --rm \
160
- --network host \
161
- -v "$CONFIG_PATH:/app/config.toml:ro" \
162
- "$DOCKERHUB_USERNAME/core-framework:latest" \
163
- alembic -c /app/alembic/notification/alembic.ini upgrade head
128
+ cf-alembic
164
129
 
165
130
  echo "Starting services with Docker Compose"
166
131
  DOCKERHUB_USERNAME="$DOCKERHUB_USERNAME" \
@@ -168,6 +133,7 @@ jobs:
168
133
  FIREBASE_CONFIG_PATH="$FIREBASE_CONFIG_PATH" \
169
134
  docker compose -f "$COMPOSE_FILE" up -d \
170
135
  --remove-orphans \
136
+ --force-recreate \
171
137
  --scale worker="${{ inputs.worker_replicas }}"
172
138
 
173
139
  echo "Cleaning up dangling Docker images"
@@ -79,6 +79,7 @@ jobs:
79
79
  with:
80
80
  context: .
81
81
  file: ./dockerfile
82
+ target: api
82
83
  push: true
83
84
  tags: ${{ steps.meta.outputs.tags }}
84
85
 
@@ -110,7 +111,8 @@ jobs:
110
111
  uses: docker/build-push-action@v6
111
112
  with:
112
113
  context: .
113
- file: ./dockerfile.worker
114
+ file: ./dockerfile
115
+ target: worker
114
116
  push: true
115
117
  tags: ${{ steps.meta.outputs.tags }}
116
118
 
@@ -100,6 +100,7 @@ jobs:
100
100
  with:
101
101
  context: .
102
102
  file: ./dockerfile
103
+ target: api
103
104
  push: false
104
105
  tags: core-framework:ci
105
106
 
@@ -107,6 +108,7 @@ jobs:
107
108
  uses: docker/build-push-action@v6
108
109
  with:
109
110
  context: .
110
- file: ./dockerfile.worker
111
+ file: ./dockerfile
112
+ target: worker
111
113
  push: false
112
114
  tags: core-framework-worker:ci
@@ -20,3 +20,7 @@ htmlcov/
20
20
 
21
21
  # Firebase
22
22
  firebase_config.json
23
+
24
+ # Cursor debug
25
+ firebase-debug.log
26
+ ui-debug.log
@@ -34,7 +34,7 @@ repos:
34
34
  - id: uv-lock
35
35
 
36
36
  - repo: https://github.com/astral-sh/ruff-pre-commit
37
- rev: v0.15.10
37
+ rev: v0.15.12
38
38
  hooks:
39
39
  - id: ruff-check
40
40
  args: [ --fix ]
@@ -0,0 +1,127 @@
1
+ # Changelog
2
+
3
+ Notable changes to **core-framework** (import **`core_framework`**). Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning is [SemVer](https://semver.org/spec/v2.0.0.html). Stable surface: **`docs/package-api.md`**.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [1.2.0] - 2026-05-02
8
+
9
+ ### Changed
10
+
11
+ - **Breaking:** Caching is **cashews-only** — removed **`RedisCache`**, **`core_framework.core.redis_cache`**, legacy **`@cache`**, **`redis_cache_status`** on readiness; **`invalidate_cache`** targets cashews keys only.
12
+ - **Breaking:** Workers use **ARQ** (not SAQ). Run **`arq core_framework.worker.main.WorkerSettings`**; tests use **`TestConfig.include_arq_fixtures`**. Queue name pinned to **`core_framework:queue`** (**`CORE_FRAMEWORK_QUEUE_NAME`**).
13
+ - **Breaking:** **`WorkerLifecycleContext`** merged into **`WorkerJobContext`**; **`worker_context`** exports **`WorkerStartupContext`** and **`WorkerJobContext`** only.
14
+
15
+ ### Internal
16
+
17
+ - Worker startup defers cashews until **`startup`** (fixes pytest + Testcontainers ordering).
18
+ - Shared idempotent **`init_firebase(settings)`** for API lifespan and worker.
19
+ - Worker schedules/tasks use **`logger.exception`** for tracebacks.
20
+
21
+ ### Documentation
22
+
23
+ - Follow feature (HTTP, notifications, stats worker, mirrors, **`docs/follow-system-design.md`**, architecture decisions, domain READMEs, **`docs/api.md`**).
24
+
25
+ ## [1.1.1] - 2026-04-24
26
+
27
+ ### Changed
28
+
29
+ - **`core_framework.testing`:** configurable ASGI lifespan timeouts for pytest / HTTP client fixtures.
30
+
31
+ ### Internal
32
+
33
+ - **`uv.lock`** refresh.
34
+
35
+ ## [1.1.0] - 2026-04-22
36
+
37
+ ### Added
38
+
39
+ - **`core-framework[testing]`** extra: pytest plugin (**`pytest11`** → **`core_framework.testing.plugin`**), **`TestConfig`**, session fixtures (Postgres, Redis, **`AsyncClient`**, migrations, Firebase helpers, optional SAQ). Parallel (**`pytest -n`**) coordination — see **`docs/testing-plugin-design.md`**. **`create_firebase_user_token`** helper.
40
+
41
+ ### Changed
42
+
43
+ - **Breaking (bootstrap):** **`core_framework.asgi:app`** is **`init_app(load_default_settings())`** at import; **`core_framework.main`** no longer exposes module-level **`app`**. **`core_framework.core`** no longer re-exports **`settings`** — use **`core_framework.core.settings`**.
44
+
45
+ ### Documentation
46
+
47
+ - **`docs/package-api.md`**, **`docs/core-framework-migration.md`**, **`README.md`**, **`docs/testing-plugin-design.md`** updated for testing and entrypoints.
48
+
49
+ ## [1.0.0] - 2026-04-18
50
+
51
+ First **SemVer-stable** release per **`docs/package-api.md`**.
52
+
53
+ ### Breaking
54
+
55
+ - **Alembic collapsed to six baselines** (`v1_ext_init` … `v1_notif_init`; order **`ALEMBIC_DOMAINS`**). **Fresh DB:** unchanged. **Already on pre-1.0 heads:** per-schema **`alembic stamp`** to the matching **`v1_*_init`** baseline before upgrading; **no supported downgrade** (baselines’ **`downgrade`** raises).
56
+ - Pre-1.0 revision IDs in older notes are **historical** only (files removed).
57
+
58
+ ### Changed
59
+
60
+ - Index tweaks in baselines (e.g. author/blocker lookup indexes; drops of unused indexes) — see git history for DDL detail.
61
+
62
+ ### Removed
63
+
64
+ - Tests no longer **downgrade-to-base** per schema (empty DB + baselines only).
65
+
66
+ ## [0.5.0] - 2026-04-17
67
+
68
+ ### Added
69
+
70
+ - **`author_context.is_notification_muted`** on post/comment when viewer is author. **`actor_id` NOT NULL** on user/moderation history (migration + backfill). **`user_restrictions_lookup`** in user schema + mirror sync with post/comment.
71
+
72
+ ### Changed
73
+
74
+ - **`GET /notifications`:** **`actor`** object replaces top-level **`actor_id`**. History endpoints require **`actor`**. **`Postgres.get_transaction_with_actor_id`** supports explicit **`actor_id`**.
75
+
76
+ ### Documentation
77
+
78
+ - Mentions flow, **`GET /users/usernames/suggest`**, **`docs/api.md`**.
79
+
80
+ ## [0.4.0] - 2026-03-26
81
+
82
+ ### Added
83
+
84
+ - Bundled Alembic in wheel (**`core_framework/alembic/`**), **`core_framework.bundled_alembic`**, console script **`cf-alembic`**.
85
+
86
+ ### Changed
87
+
88
+ - Script renamed from **`cff-migrate`** to **`cf-alembic`**. **`pyproject`**: dropped **`readme`** from **`[project]`** for lean Docker builds. OpenAPI title **`core-framework API`**. **`main.app`** / worker **`task_worker`** import-time behavior adjusted (SAQ). Application DI: domain modules as namespaces; **`configure_application_dependencies`**; cashews wiring.
89
+
90
+ ### Fixed
91
+
92
+ - **Worker Docker image:** builder copies **`alembic/`** for Hatch force-include.
93
+
94
+ ### CI
95
+
96
+ - **package-smoke** and **docker-build** on PRs; Docker Hub image name **`core-framework`**.
97
+
98
+ ### Documentation
99
+
100
+ - **`README`**, **`docs/core-framework-migration.md`**, **`docs/package-api.md`**, **`docs/architecture.md`** (runtime / bootstrap).
101
+
102
+ ### Removed
103
+
104
+ - **`load_default_runtime()`**, **`DependencyHolder`**, **`core_framework.core.runtime`** package attribute shadowing the runtime module.
105
+
106
+ ## [0.3.0] - 2026-03-25
107
+
108
+ ### Added
109
+
110
+ - **`init_app(settings)`**, **`create_task_worker(settings)`**, **`CoreRuntime`**, **`build_core_runtime`**, **`configure_application_dependencies`**, **`load_default_settings()`**, **`configure_core_runtime`**. **`docs/package-api.md`**.
111
+
112
+ ### Changed
113
+
114
+ - Embedding: prefer full bootstrap via **`init_app`**; **`/readiness`** needs **`app.state.core_runtime`**. Pagination, event tokens, media URLs, cache fail fast if not configured.
115
+
116
+ ### Migration
117
+
118
+ - Host: **`Settings`** from host config → **`init_app(settings)`**; **`configure_core_runtime(runtime)`** if using **`core_framework.core`** globals directly.
119
+
120
+ [0.3.0]: https://github.com/NepNepFFXIV/core-framework/compare/v0.2.0...v0.3.0
121
+ [0.4.0]: https://github.com/NepNepFFXIV/core-framework/compare/v0.3.0...v0.4.0
122
+ [0.5.0]: https://github.com/NepNepFFXIV/core-framework/compare/v0.4.0...v0.5.0
123
+ [1.0.0]: https://github.com/NepNepFFXIV/core-framework/compare/v0.5.0...v1.0.0
124
+ [1.1.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.0.0...v1.1.0
125
+ [1.1.1]: https://github.com/NepNepFFXIV/core-framework/compare/v1.1.0...v1.1.1
126
+ [1.2.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.1.1...v1.2.0
127
+ [unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v1.2.0...HEAD