core-framework 2.3.0__tar.gz → 2.3.2__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.
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/api-reference-docs.mdc +9 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/constitution.mdc +1 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/error-boundary.mdc +3 -3
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/no-code-in-docs.mdc +1 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/postgres-config-conventions.mdc +9 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/repository-schema-isolation.mdc +21 -8
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/skills/add-domain/SKILL.md +1 -1
- core_framework-2.3.2/.cursor/skills/implement-feature/SKILL.md +93 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/skills/sync-host-rules/SKILL.md +5 -5
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/skills/write-flow-doc/SKILL.md +2 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/CHANGELOG.md +40 -5
- {core_framework-2.3.0 → core_framework-2.3.2}/PKG-INFO +3 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/README.md +2 -0
- core_framework-2.3.2/alembic/analytics/alembic/versions/585105e1149e_analytics_add_utm_events.py +38 -0
- core_framework-2.3.2/alembic/user/alembic/versions/44df70a45ba6_user_add_signup_events.py +32 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/config.toml +1 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/config.toml.template +1 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/outbox/schemas.py +7 -3
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/auth/schemas.py +1 -12
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/dependencies.py +12 -14
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/read_service.py +25 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/sources/comment_created.py +7 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/sources/post_created.py +7 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/sources/post_viewed.py +7 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/sources/user_registered.py +7 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/auth/access_service.py +2 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/auth/models.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/bootstrap.py +0 -5
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/cache/models.py +4 -0
- core_framework-2.3.2/core_framework/application/events/README.md +29 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/events/event_service.py +48 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/events/event_token.py +2 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/events/models.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/media/attachment_read.py +1 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/media/attachment_service.py +15 -3
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/media/read_url_config.py +4 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/notifications/enums.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/outbox/events.py +9 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/shared/enums.py +9 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/shared/exceptions.py +20 -0
- core_framework-2.3.2/core_framework/application/shared/request_context.py +66 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/users/admin_service.py +0 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/users/deletion_audit.py +1 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/users/enums.py +4 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/users/public_service.py +4 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/users/user_deletion_service.py +21 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/cache.py +18 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/database.py +5 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/analytics/README.md +17 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/analytics/__init__.py +7 -2
- core_framework-2.3.2/core_framework/domains/analytics/constants.py +15 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/analytics/dependencies.py +6 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/analytics/models.py +6 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/analytics/ports/activity_source.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/analytics/repository.py +95 -13
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/analytics/service.py +84 -2
- core_framework-2.3.2/core_framework/domains/auth/README.md +68 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/auth/ports/auth.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/README.md +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/dependencies.py +6 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/repository.py +219 -208
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/service.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/README.md +3 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/components/finals_publisher.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/components/staging.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/components/variant_encoder.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/ports/finals_publisher.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/ports/staging.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/ports/variant_encoder.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/service.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/components/finals_publisher.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/components/staging.py +3 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/components/variant_encoder.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/ports/finals_publisher.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/ports/staging.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/ports/variant_encoder.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/service.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/components/finals_publisher.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/components/staging.py +3 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/components/variant_encoder.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/ports/finals_publisher.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/ports/staging.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/ports/variant_encoder.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/service.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/dependencies.py +6 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/shared/repository.py +63 -52
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/shared/service.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/README.md +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/dependencies.py +14 -6
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/outbox_repository.py +19 -11
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/repository.py +104 -102
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/service.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/notification/README.md +3 -3
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/notification/dependencies.py +6 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/notification/repository.py +85 -74
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/notification/service.py +2 -0
- core_framework-2.3.2/core_framework/domains/outbox/README.md +65 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/outbox/ports/consumer.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/outbox/ports/producer.py +4 -4
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/README.md +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/dependencies.py +6 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/repository.py +238 -227
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/service.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/README.md +7 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/dependencies.py +14 -6
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/outbox_repository.py +19 -11
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/repository.py +153 -126
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/service.py +23 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/attachment_staging_display_geometry.py +4 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/local_attachment_staging_adapter.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/local_banner_staging_adapter.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/local_staging_adapter.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/pillow_attachment_variant_encoder_adapter.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/pillow_avatar_variant_encoder_adapter.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/pillow_banner_variant_encoder_adapter.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/ssh_attachment_finals_publisher_adapter.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/ssh_banner_finals_publisher_adapter.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/ssh_finals_publisher_adapter.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/outbox/outbox_consumer_adapter.py +21 -10
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/outbox/outbox_producer_adapter.py +6 -4
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/auth.py +20 -30
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/main.py +6 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/analytics_refresh_cron.py +2 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_expired_account_deletions.py +1 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_expired_mute_lifts.py +1 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/worker_context.py +4 -0
- core_framework-2.3.2/docs/README.md +46 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/README.md +1 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/deploy-playbook.md +2 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/e2e-smoke-tests-design.md +1 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/edge-upload-limits.md +1 -1
- core_framework-2.3.2/docs/design/README.md +30 -0
- {core_framework-2.3.0/docs/platform → core_framework-2.3.2/docs/design}/analytics-rollup-design.md +40 -45
- core_framework-2.3.2/docs/design/cache-invalidation-design.md +141 -0
- {core_framework-2.3.0/docs/platform → core_framework-2.3.2/docs/design}/create-idempotency-design.md +19 -18
- {core_framework-2.3.0/docs/platform → core_framework-2.3.2/docs/design}/event-outbox-design.md +50 -169
- core_framework-2.3.2/docs/design/events-ingest-design.md +202 -0
- {core_framework-2.3.0/docs/platform → core_framework-2.3.2/docs/design}/media-storage-topology.md +2 -2
- {core_framework-2.3.0/docs/platform → core_framework-2.3.2/docs/design}/outbox-admin-ops-design.md +3 -3
- core_framework-2.3.2/docs/design/utm-events-design.md +233 -0
- core_framework-2.3.2/docs/domains/README.md +23 -0
- core_framework-2.3.2/docs/domains/analytics/admin_analytics_reads.md +135 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/auth/access_control.md +1 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/auth/registration.md +4 -3
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/cache/admin_cache_invalidation.md +11 -50
- core_framework-2.3.2/docs/domains/comments/README.md +31 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/comments/admin_comments.md +6 -5
- core_framework-2.3.2/docs/domains/comments/comment_like.md +96 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/comments/comment_report.md +4 -3
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/comments/comment_stats_aggregation.md +6 -7
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/comments/create_comment.md +22 -18
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/comments/edit_comment.md +7 -5
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/comments/retrieve_comments.md +13 -9
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/deletion/comments.md +1 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/deletion/overview.md +11 -3
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/deletion/posts.md +1 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/deletion/users.md +110 -68
- core_framework-2.3.2/docs/domains/events/events.md +231 -0
- core_framework-2.3.2/docs/domains/media/README.md +36 -0
- core_framework-2.3.2/docs/domains/media/attachment_upload.md +73 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/media/avatar-upload-design.md +9 -9
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/media/banner-upload-design.md +14 -14
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/media/post-comment-attachments-design.md +10 -10
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/media/upload-pipeline-design.md +51 -46
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/media/upload_pipeline.md +20 -5
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/mentions/mentions_in_content.md +12 -11
- core_framework-2.3.2/docs/domains/moderation/README.md +28 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/moderation/appeals.md +18 -8
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/moderation/internal_notes.md +11 -10
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/moderation/moderator_actions.md +6 -5
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/moderation/reports.md +10 -8
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/moderation/restrictions.md +55 -23
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/notifications/notification_inbox.md +19 -17
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/outbox/admin_outbox_ops.md +8 -6
- core_framework-2.3.2/docs/domains/posts/README.md +33 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/posts/admin_posts.md +8 -6
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/posts/author_context.md +1 -1
- core_framework-2.3.2/docs/domains/posts/create_post.md +112 -0
- core_framework-2.3.2/docs/domains/posts/edit_post.md +80 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/posts/hashtag_discovery.md +6 -4
- core_framework-2.3.2/docs/domains/posts/post_report.md +105 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/posts/post_stats_aggregation.md +7 -8
- core_framework-2.3.2/docs/domains/posts/retrieve_posts.md +190 -0
- core_framework-2.3.2/docs/domains/users/account.md +106 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/account_deletion.md +6 -7
- core_framework-2.3.2/docs/domains/users/admin_users.md +166 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/avatar.md +1 -3
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/banner.md +7 -8
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/blocks.md +10 -9
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/change_history.md +5 -4
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/check_username_exists.md +3 -3
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/follow-system-design.md +26 -32
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/follow.md +13 -10
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/my_posts_and_comments.md +4 -4
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/preferences.md +1 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/public_profile.md +5 -5
- core_framework-2.3.2/docs/domains/users/user_removal.md +86 -0
- core_framework-2.3.2/docs/domains/users/user_stats_aggregation.md +79 -0
- core_framework-2.3.2/docs/library/README.md +15 -0
- core_framework-2.3.2/docs/library/api.md +212 -0
- core_framework-2.3.2/docs/library/capability-wiring.md +123 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/library/core-framework-migration.md +21 -65
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/library/overview.md +19 -17
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/library/package-api.md +9 -7
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/library/testing-plugin-design.md +30 -32
- core_framework-2.3.2/docs/library/workers.md +81 -0
- core_framework-2.3.2/docs/platform/README.md +18 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/platform/architecture-decisions.md +11 -13
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/platform/client-error-visibility.md +100 -56
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/platform/conventions.md +22 -1
- core_framework-2.3.2/docs/platform/domain-services-and-adapters.md +60 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/platform/patch-update-typed-payload.md +2 -1
- core_framework-2.3.2/docs/testing.md +150 -0
- core_framework-2.3.2/docs/todo.md +75 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/pyproject.toml +2 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/analytics/_helpers.py +9 -4
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/_outbox_helpers.py +4 -7
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/moderation/router_test.py +13 -7
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/posts/router_test.py +14 -8
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/users/router_test.py +9 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/comments/comment_attachments_integration_test.py +236 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/create_idempotency_integration_test.py +16 -10
- core_framework-2.3.2/tests/integration/api/events/router_test.py +543 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/notifications/router_test.py +9 -4
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/posts/post_attachments_integration_test.py +294 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/posts/post_deletion_integration_test.py +14 -9
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/posts/post_stats_dirty_marking_test.py +2 -2
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/users/deletion_audit_integration_test.py +6 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/users/user_deletion_integration_test.py +134 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/account_deletion_test.py +16 -8
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/create_idempotency_retention_test.py +14 -8
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/moderation_outbox_test.py +15 -7
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/stale_staging_sweep_test.py +6 -1
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/application/events/event_service_test.py +47 -1
- core_framework-2.3.2/tests/unit/application/shared/request_context_test.py +30 -0
- core_framework-2.3.2/tests/unit/docs/_api_reference.py +67 -0
- core_framework-2.3.2/tests/unit/docs/api_routes_test.py +54 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/domains/user/service_test.py +46 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/uv.lock +101 -12
- core_framework-2.3.0/.cursor/skills/implement-feature/SKILL.md +0 -66
- core_framework-2.3.0/core_framework/application/events/README.md +0 -35
- core_framework-2.3.0/core_framework/application/shared/user_agent.py +0 -25
- core_framework-2.3.0/core_framework/domains/analytics/constants.py +0 -3
- core_framework-2.3.0/docs/README.md +0 -28
- core_framework-2.3.0/docs/domains/README.md +0 -18
- core_framework-2.3.0/docs/domains/events/events.md +0 -126
- core_framework-2.3.0/docs/domains/users/account.md +0 -61
- core_framework-2.3.0/docs/domains/users/user_removal.md +0 -129
- core_framework-2.3.0/docs/library/README.md +0 -11
- core_framework-2.3.0/docs/library/api.md +0 -205
- core_framework-2.3.0/docs/platform/README.md +0 -19
- core_framework-2.3.0/docs/platform/domain-services-and-adapters.md +0 -116
- core_framework-2.3.0/docs/platform/events-ingest-design.md +0 -109
- core_framework-2.3.0/docs/todo.md +0 -11
- core_framework-2.3.0/tests/integration/api/events/router_test.py +0 -180
- {core_framework-2.3.0 → core_framework-2.3.2}/.agents/skills/fastapi +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.agents/skills/library-skills/.library-skills.json +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.agents/skills/library-skills/SKILL.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/alembic-migrations.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/api-layer.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/api-validation.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/application-layer.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/constants-final.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/database-triggers.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/docstrings.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/domain-caller-context.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/domain-imports.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/domain-input-guards.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/domain-repository-exceptions.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/domain-services-and-adapters.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/flow-documentation.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/inline-raise-messages.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/integration-test-strategy.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/layer-boundaries.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/strong-reads.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/rules/structured-logging.mdc +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/skills/add-config/SKILL.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/skills/code-review/SKILL.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/skills/fastapi +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.cursor/skills/recommend-features/SKILL.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.dockerignore +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.github/workflows/_deploy.yml +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.github/workflows/dev-ci-cd.yaml +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.github/workflows/manual-deployment.yaml +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.github/workflows/publish-pypi.yml +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.github/workflows/test.yaml +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.gitignore +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.pre-commit-config.yaml +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/.python-version +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/LICENSE +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/analytics/alembic/README +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/analytics/alembic/env.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/analytics/alembic/script.py.mako +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/analytics/alembic/versions/5b9ac98bb150_analytics_daily_fact.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/analytics/alembic/versions/7f2a9c1d4e8b_analytics_daily_fact_day_index.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/analytics/alembic.ini +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic/README +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic/env.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic/script.py.mako +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic/versions/comment_add_comment_attachments.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency_created_at_index.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic/versions/comment_add_comment_mentions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic/versions/comment_tombstone_status_model.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic/versions/v1_comment_init_baseline.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/comment/alembic.ini +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/extension/alembic/README +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/extension/alembic/env.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/extension/alembic/script.py.mako +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/extension/alembic/versions/v1_ext_init_baseline.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/extension/alembic.ini +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/media/alembic/README +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/media/alembic/env.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/media/alembic/script.py.mako +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/media/alembic/versions/media_add_attachment_staging_registry.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/media/alembic/versions/media_add_banner_staging_and_lease.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/media/alembic/versions/media_attachment_staging_registry_published_at.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/media/alembic/versions/v1_media_init_baseline.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/media/alembic.ini +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/moderation/alembic/README +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/moderation/alembic/env.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/moderation/alembic/script.py.mako +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/moderation/alembic/versions/moderation_add_event_outbox.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/moderation/alembic/versions/moderation_add_event_outbox_dead_indexes.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/moderation/alembic/versions/moderation_add_event_outbox_pending_created_at_index.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/moderation/alembic.ini +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/notification/alembic/README +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/notification/alembic/env.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/notification/alembic/script.py.mako +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/notification/alembic/versions/v1_notif_init_baseline.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/notification/alembic.ini +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/README +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/env.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/script.py.mako +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/versions/post_add_post_attachments.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/versions/post_add_post_create_idempotency.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/versions/post_add_post_create_idempotency_created_at_index.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/versions/post_add_post_mentions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/versions/post_post_views_retention_high_water_mark.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/versions/post_tombstone_status_model.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic/versions/v1_post_init_baseline.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/post/alembic.ini +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/README +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/env.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/script.py.mako +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/versions/user_add_avatar_ingest_sequences.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/versions/user_add_banner_ingest_sequences.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/versions/user_add_date_of_birth_to_profiles.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/versions/user_add_event_outbox.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/versions/user_add_event_outbox_dead_indexes.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/versions/user_add_event_outbox_pending_created_at_index.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/versions/user_reserve_sentinel_user_ids.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic/versions/v1_user_init_baseline.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/alembic/user/alembic.ini +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/analytics/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/analytics/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/cache/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/cache/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/comments/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/comments/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/moderation/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/moderation/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/moderation/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/outbox/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/posts/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/posts/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/users/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/users/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/admin/users/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/attachments/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/attachments/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/auth/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/auth/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/comments/authenticated/mappers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/comments/authenticated/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/comments/authenticated/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/comments/public/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/comments/public/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/comments/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/comments/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/constants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/events/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/events/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/idempotency/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/idempotency/dependencies.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/notifications/authenticated/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/notifications/authenticated/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/notifications/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/posts/authenticated/mappers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/posts/authenticated/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/posts/authenticated/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/posts/public/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/posts/public/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/posts/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/posts/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/system/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/system/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/authenticated/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/authenticated/mappers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/authenticated/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/authenticated/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/mappers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/public/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/public/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/public/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/router.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/api/users/shared/schemas.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/bucket_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/prune_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/refresh_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/registry.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/auth/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/auth/auth_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/cache/invalidate_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/comments/admin_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/comments/aggregation_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/comments/authenticated_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/comments/cleanup_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/comments/public_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/media/README.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/media/avatar_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/media/banner_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/media/ingest_guards.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/moderation/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/moderation/appeal_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/moderation/moderator_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/moderation/report_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/moderation/scheduled_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/moderation/user_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/notifications/README.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/notifications/inbox_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/notifications/mute_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/notifications/notification_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/outbox/block_mirrors.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/outbox/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/outbox/follow_mirrors.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/outbox/handlers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/outbox/ops_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/outbox/polling_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/outbox/processing_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/outbox/restriction_mirrors.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/posts/admin_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/posts/aggregation_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/posts/authenticated_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/posts/cleanup_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/posts/public_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/shared/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/shared/worker_jobs.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/users/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/users/aggregation_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/users/authenticated_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/users/scheduled_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/asgi.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/bundled_alembic.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/constants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/context.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/exception_handlers/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/exception_handlers/application.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/exception_handlers/common.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/exception_handlers/setup.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/firebase.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/http_client.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/logging.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/middleware.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/observability.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/pagination.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/redis.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/runtime.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/core/settings.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/analytics/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/auth/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/auth/dependencies.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/auth/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/auth/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/auth/ports/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/auth/service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/constants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/enums.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/comment/outcomes.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/constants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/attachment/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/components/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/constants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/avatar/ports/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/constants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/banner/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/shared/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/shared/constants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/shared/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/shared/outcomes.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/media/shared/utils.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/enums.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/outbox_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/moderation/outcomes.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/notification/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/notification/enums.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/notification/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/notification/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/outbox/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/outbox/dependencies.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/outbox/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/outbox/ports/admin.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/outbox/service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/constants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/enums.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/post/outcomes.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/constants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/enums.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/exceptions.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/models.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/outbox_service.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/outcomes.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/user/utils.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/domains/utils.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/firebase/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/firebase/firebase_auth_adapter.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/infrastructure/media/pillow_staging_image.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/main.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/migrate_cli.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/arq.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/config.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/containers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/firebase.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/firebase_identity_toolkit.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/hookspecs.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/httpx_test_client.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/media.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/migrations.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/testing/plugin.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_aggregate_post_stats.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_aggregate_user_stats.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_poll_outbox.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_prune_analytics_daily_facts.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_prune_post_views.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_purge_expired_create_idempotency_keys.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_refresh_analytics.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_sweep_stale_attachment_staging.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_sweep_stale_avatar_staging.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/schedules/schedule_sweep_stale_banner_staging.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/delete_attachment_finals.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/delete_superseded_avatar_finals.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/delete_superseded_banner_finals.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_account_deletion.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_admin_user_deletion.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_aggregate_comment_stats.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_aggregate_post_stats.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_aggregate_user_stats.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_attachment_asset_variants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_banner_asset_variants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_cache_pattern_invalidation.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_destructive_comment_delete.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_destructive_post_delete.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_media_asset_variants.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_mute_lift.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_outbox_row.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_refresh_analytics_bucket.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/core_framework/worker/tasks/process_user_content_deletion.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docker-compose.dev.yaml +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docker-compose.yml +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/dockerfile +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/examples/Caddyfile.example +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/examples/image-server-known-hosts.example +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/guides/app-server-deploy-ssh.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/guides/image-server-ssh.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/guides/postgres-setup.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/guides/redis-setup.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/guides/ubuntu-server-setup.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/deployments/release-playbook.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/comments/delete_comment.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/posts/post_like.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/posts/post_visibility.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/domains/users/profile.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/platform/database-triggers.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/docs/platform/layers-and-boundaries.md +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/firebase_config.example.json +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/makefile +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/conftest.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/_auth.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/_canaries.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/_media.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/_settings.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/_upload_payloads.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/conftest.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/media/pipeline_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/smoke/canary_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/smoke/media_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/smoke/system_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/e2e/smoke/tls_host_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/_http_error_details.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/_auth_helpers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/_destructive_delete_helpers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/_http_helpers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/_user_deletion_helpers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/_user_helpers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/_auth_cases.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/analytics/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/analytics/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/cache/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/cache/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/comments/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/comments/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/moderation/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/moderation/_auth_cases.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/outbox/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/outbox/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/posts/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/admin/users/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/attachments/_helpers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/attachments/attachment_upload_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/auth/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/auth/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/comments/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/comments/authenticated/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/comments/public/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/comments/public/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/media/ingest_contract_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/mentions/mentions_integration_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/notifications/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/posts/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/posts/authenticated/post_writes_integration_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/posts/comment_count_aggregation_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/posts/followers_visibility_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/posts/public/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/posts/public/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/public_read_by_id_unavailable_detail_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/system/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/system/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/users/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/users/authenticated/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/users/authenticated/avatar_upload_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/users/authenticated/banner_upload_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/users/authenticated/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/users/public/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/api/users/public/router_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/_media_upload_helpers.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/aggregate_comment_stats_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/aggregate_post_stats_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/aggregate_user_stats_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/analytics_prune_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/analytics_refresh_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/attachment_upload_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/avatar_upload_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/banner_upload_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/conftest.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/mute_lift_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/user_outbox_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/integration/worker/utils_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/api/comments/authenticated/comment_request_schemas_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/api/dedupe_preserving_order_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/api/posts/authenticated/post_update_mapper_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/api/users/profile_update_mapper_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/application/comments/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/application/notifications/inbox_service_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/application/users/deletion_audit_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/core/bundled_alembic_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/core/migrate_cli_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/core/pagination_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/domains/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/domains/comment/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/domains/post/models_test.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/infrastructure/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/infrastructure/media/__init__.py +0 -0
- {core_framework-2.3.0 → core_framework-2.3.2}/tests/unit/infrastructure/media/ssh_finals_publisher_adapter_test.py +0 -0
|
@@ -29,3 +29,12 @@ globs:
|
|
|
29
29
|
- Detailed behavior notes
|
|
30
30
|
|
|
31
31
|
For detailed API behavior, create or update flow documentation in `docs/domains/` instead.
|
|
32
|
+
|
|
33
|
+
## CI contract
|
|
34
|
+
|
|
35
|
+
`tests/unit/docs/api_routes_test.py` parses this file and asserts **bidirectional** parity with the core FastAPI app:
|
|
36
|
+
|
|
37
|
+
- every listed `(METHOD, path)` exists on the app with **matching path-param names**
|
|
38
|
+
- every app `(METHOD, path)` (GET/POST/PUT/PATCH/DELETE) is listed here
|
|
39
|
+
|
|
40
|
+
Flow docs under `docs/domains/` must not list endpoints in their `http` blocks unless those exact routes appear here. Update this index when adding or renaming routes.
|
|
@@ -39,4 +39,4 @@ Do not edit consuming host repos in the same task as core-framework work unless
|
|
|
39
39
|
|
|
40
40
|
## Feature implementation
|
|
41
41
|
|
|
42
|
-
Greenfield features (DB → API → repository → service → tests): use the **implement-feature** skill — one layer per response unless the user says otherwise.
|
|
42
|
+
Greenfield features (DB → API → repository → service → **documentation** → tests): use the **implement-feature** skill — one layer per response unless the user says otherwise. A feature is **not done** until documentation is updated.
|
|
@@ -16,10 +16,10 @@ Platform contract: **`docs/platform/client-error-visibility.md`**. Domain import
|
|
|
16
16
|
|
|
17
17
|
| Layer | Outward errors | HTTP |
|
|
18
18
|
|-------|----------------|------|
|
|
19
|
-
| **Application** | **`application.shared.exceptions`**, **`InvalidPageTokenError`** | Handlers map type → status +
|
|
19
|
+
| **Application** | **`application.shared.exceptions`**, **`InvalidPageTokenError`** | Handlers map type → status + catalog **`detail`** |
|
|
20
|
+
| **API** | Pydantic; **`HTTPException`** for transport-local guards | **422** field errors; router-chosen status + **`detail`** |
|
|
20
21
|
| **Domain typed** | Internal — not imported by API/application | No typed handlers |
|
|
21
22
|
| **Domain `BaseXxxException`** | Handler catch-all only | **500** if leaked |
|
|
22
|
-
| **API** | Pydantic | **422** |
|
|
23
23
|
|
|
24
24
|
## Application and domain
|
|
25
25
|
|
|
@@ -47,7 +47,7 @@ Platform contract: **`docs/platform/client-error-visibility.md`**. Domain import
|
|
|
47
47
|
|
|
48
48
|
## Validation order
|
|
49
49
|
|
|
50
|
-
User-fixable input shape → API schemas (**422**). Domain backstops → **`DomainValidationError`** → generic **400
|
|
50
|
+
User-fixable input shape → API schemas (**422**) or router **`HTTPException`**. Use-case outcomes → application exceptions + handlers. Domain backstops → **`DomainValidationError`** → generic **400** (never domain text in **`detail`**).
|
|
51
51
|
|
|
52
52
|
## Logging
|
|
53
53
|
|
|
@@ -28,7 +28,7 @@ Documentation under **`docs/`** must not carry **implementation** snippets that
|
|
|
28
28
|
## Exceptions
|
|
29
29
|
|
|
30
30
|
- **Deployment runbooks** under **`docs/deployments/`** (setup guides, deploy/release playbooks, SSH/image-server guides) may include fenced **`bash`** shell blocks. These are operator instructions for interactive server setup, not implementation that drifts from source; copy-paste commands are the practical content. This exemption does **not** extend to **`docs/deployments/*-design.md`** design docs, which follow the general rules above.
|
|
31
|
-
- **Design docs** under **`docs/platform
|
|
31
|
+
- **Design docs** under **`docs/design/`**, **`docs/platform/`**, and **`docs/library/`** (and **`docs/deployments/*-design.md`**) follow the general allow/exclude lists — DDL is allowed, DML and Python are not. Flow docs under **`docs/domains/**`** are stricter: see **`flow-documentation.mdc`** (no DDL, no JSON request bodies, fields described as text/tables).
|
|
32
32
|
|
|
33
33
|
## Rationale
|
|
34
34
|
|
|
@@ -45,3 +45,12 @@ Use explicit write/read configuration and keep schema names shared.
|
|
|
45
45
|
- `read_postgres`
|
|
46
46
|
- Domain dependencies pass `(write, read, strong)` explicitly to repositories.
|
|
47
47
|
- Until a dedicated strong instance exists, use write instance for the strong argument intentionally.
|
|
48
|
+
|
|
49
|
+
## Schema names in SQL
|
|
50
|
+
|
|
51
|
+
- Schema names come only from `settings.postgres_schemas` (see **Settings Model** above).
|
|
52
|
+
- Domain `dependencies.py` passes unquoted `schema=runtime.settings.postgres_schemas.schema_<domain>` into repositories (keyword-only `__init__`).
|
|
53
|
+
- Repositories cache `self.schema_sql = quote_postgres_identifier(schema)` and use `{self.schema_sql}.table` in f-string SQL — never hard-coded `"post".` literals.
|
|
54
|
+
- Outbox producer adapter is wired per domain with `schema` at construction; consumer adapter receives unquoted `schema` per call and caches quoted SQL.
|
|
55
|
+
|
|
56
|
+
Repository isolation rules (no cross-domain schema access): **`repository-schema-isolation.mdc`**.
|
|
@@ -11,10 +11,19 @@ Each domain repository may only read/write tables in **its own** Postgres schema
|
|
|
11
11
|
|
|
12
12
|
Canonical rule: `docs/platform/layers-and-boundaries.md` — *no cross-domain schema reads, writes, joins, or foreign keys*.
|
|
13
13
|
|
|
14
|
+
## Schema qualification
|
|
15
|
+
|
|
16
|
+
Repositories receive unquoted `schema` from `runtime.settings.postgres_schemas` in `dependencies.py` (keyword-only `__init__` arg). In `__init__`:
|
|
17
|
+
|
|
18
|
+
- `self.schema` — config schema name (e.g. `post`)
|
|
19
|
+
- `self.schema_sql` — `quote_postgres_identifier(schema)` for SQL interpolation
|
|
20
|
+
|
|
21
|
+
Use `{self.schema_sql}.table` in f-string SQL — not hard-coded `"post".` literals.
|
|
22
|
+
|
|
14
23
|
## Allowed in a repository
|
|
15
24
|
|
|
16
|
-
- SQL qualified with that domain's schema only (e.g. `
|
|
17
|
-
- **Within-schema** policy gates (e.g. `insert_post_like` checks `
|
|
25
|
+
- SQL qualified with that domain's schema only (e.g. `{self.schema_sql}.posts` in `PostRepository`).
|
|
26
|
+
- **Within-schema** policy gates (e.g. `insert_post_like` checks `{self.schema_sql}.posts.status = active`).
|
|
18
27
|
- **Lookup mirrors** in the same schema (`user_blocks_lookup`, `user_restrictions_lookup`, `user_followers_lookup`) — synced by application flows; mirrors are not authoritative.
|
|
19
28
|
|
|
20
29
|
## Forbidden in a repository
|
|
@@ -23,18 +32,18 @@ Canonical rule: `docs/platform/layers-and-boundaries.md` — *no cross-domain sc
|
|
|
23
32
|
- Importing another domain's `service`, `repository`, or models to enforce policy.
|
|
24
33
|
|
|
25
34
|
```python
|
|
26
|
-
# ❌ moderation/repository.py — cross-domain EXISTS
|
|
27
|
-
insert into
|
|
35
|
+
# ❌ moderation/repository.py — cross-domain EXISTS (illustrative schema literals)
|
|
36
|
+
insert into {moderation_schema_sql}.post_reports (...)
|
|
28
37
|
select $1, $2, $3, $4
|
|
29
38
|
where exists (
|
|
30
|
-
select 1 from
|
|
39
|
+
select 1 from {post_schema_sql}.posts
|
|
31
40
|
where id = $2 and status = 'active'
|
|
32
41
|
)
|
|
33
42
|
|
|
34
43
|
# ❌ notification/repository.py — cross-domain mute gate
|
|
35
|
-
insert into
|
|
44
|
+
insert into {notification_schema_sql}.notification_mutes (...)
|
|
36
45
|
select ...
|
|
37
|
-
where exists (select 1 from
|
|
46
|
+
where exists (select 1 from {comment_schema_sql}.comments where ...)
|
|
38
47
|
```
|
|
39
48
|
|
|
40
49
|
## Alembic migrations (different convention)
|
|
@@ -69,4 +78,8 @@ Before adding a repository write that depends on another domain's row state:
|
|
|
69
78
|
|
|
70
79
|
## Self-check (repositories only)
|
|
71
80
|
|
|
72
|
-
In `domains/<name>/repository.py
|
|
81
|
+
In `domains/<name>/repository.py`:
|
|
82
|
+
|
|
83
|
+
- Every table/type qualifier in SQL must use `{self.schema_sql}.…` (that domain's schema only).
|
|
84
|
+
- Do not hard-code `"user".`, `"post".`, etc. — use `self.schema` / `self.schema_sql` from config.
|
|
85
|
+
- Mirror lookup tables (`user_blocks_lookup`, etc.) still live under `{self.schema_sql}` when they belong to that domain's schema.
|
|
@@ -18,7 +18,7 @@ Follow the **implement-feature** skill: one step per response, wait for review.
|
|
|
18
18
|
Not every domain owns persistence. A **capability-only** domain wraps a non-SQL capability (for example `auth`, which wraps Firebase Admin via `domains/auth/ports/`, `domains/auth/service.py`, and `core_framework/infrastructure/firebase/`). For those, **skip** schema config, the Alembic tree, and `ALEMBIC_DOMAINS` entirely — no `schema_<domain>` in `PostgresSchemasConfig` / `[postgres_schemas]`, no `alembic/<domain>/`, no repository. Scaffold only:
|
|
19
19
|
|
|
20
20
|
- `domains/<domain>/{__init__.py, exceptions.py, models.py, ports/, service.py, dependencies.py}`
|
|
21
|
-
- A port (`<Name>Port`) describing the capability, a facade `*Service` over it, and an adapter under `core_framework/infrastructure/<domain>/` wired in `dependencies.py` — see `docs/platform/domain-services-and-adapters.md` (
|
|
21
|
+
- A port (`<Name>Port`) describing the capability, a facade `*Service` over it, and an adapter under `core_framework/infrastructure/<domain>/` wired in `dependencies.py` — see `docs/platform/domain-services-and-adapters.md` (rules) and `docs/library/capability-wiring.md` (Auth example).
|
|
22
22
|
- Export only `Base<Domain>Exception` + public models from `__init__.py`; keep the facade service in `dependencies.py`.
|
|
23
23
|
- Wire `configure_<domain>_dependencies()` in `application/bootstrap.py` (no `runtime` needed when there is no Postgres handle).
|
|
24
24
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: implement-feature
|
|
3
|
+
description: Step-by-step feature implementation — DB → API → repository → service/application → documentation → tests. Use when adding a new feature, endpoint, or domain behavior across layers, or when the user asks to follow the implementation workflow.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Implement Feature
|
|
7
|
+
|
|
8
|
+
Implement **one step at a time**. Stop after each step and wait for user review. Do **not** skip ahead or implement multiple layers in one response unless the user **explicitly** says "implement everything" or "do it all at once".
|
|
9
|
+
|
|
10
|
+
## Definition of done
|
|
11
|
+
|
|
12
|
+
A feature is **not complete** until **all** documentation for the shipped behavior is updated. Code merged without matching docs is **unfinished work** — do not treat the feature as done or move on until the documentation step is accepted.
|
|
13
|
+
|
|
14
|
+
## Before you start
|
|
15
|
+
|
|
16
|
+
- Do **not** implement repository, service, or application code when doing the API layer.
|
|
17
|
+
- Do **not** implement multiple layers in a single response.
|
|
18
|
+
- When in doubt, do **less** and wait for review.
|
|
19
|
+
|
|
20
|
+
## 1. Database changes (if needed)
|
|
21
|
+
|
|
22
|
+
- **Only** create the Alembic migration file(s).
|
|
23
|
+
- **No** code changes anywhere else.
|
|
24
|
+
- Wait for the user to review and accept the database structure before proceeding.
|
|
25
|
+
|
|
26
|
+
## 2. API layer (if needed)
|
|
27
|
+
|
|
28
|
+
After the user accepts database changes (or if there were none):
|
|
29
|
+
|
|
30
|
+
- **Only** implement the API layer: schemas and router.
|
|
31
|
+
- Follow the **fastapi** skill (`.agents/skills/fastapi/`) and **`api-layer`** / **`api-validation`** / **`error-boundary`** rules.
|
|
32
|
+
- **No** changes in domain, service, or application layers.
|
|
33
|
+
- Include request/response schemas, error handling, and HTTP-related logic.
|
|
34
|
+
- If the feature is not yet implemented, return 501 Not Implemented from the route.
|
|
35
|
+
- Wait for the user to review and accept before proceeding.
|
|
36
|
+
|
|
37
|
+
## 3. Repository and domain model
|
|
38
|
+
|
|
39
|
+
After the user accepts the API layer (or if there was no API layer):
|
|
40
|
+
|
|
41
|
+
- Implement the **Repository** for the domain layer (Postgres only).
|
|
42
|
+
- Include the **domain model** if data is retrieved from the database.
|
|
43
|
+
- If the feature needs non-SQL I/O (filesystem, HTTP, SSH), add **ports** in the domain and **adapter implementations** under `core_framework/infrastructure/<domain>/` per `docs/platform/domain-services-and-adapters.md` — not in the repository.
|
|
44
|
+
- Wait for the user to review and accept before proceeding.
|
|
45
|
+
|
|
46
|
+
## 4. Service and application layer
|
|
47
|
+
|
|
48
|
+
After the user accepts the repository (and any ports/adapters):
|
|
49
|
+
|
|
50
|
+
- Implement **domain service(s)** (persistence and any port-backed collaborators); wire in `dependencies.py`.
|
|
51
|
+
- Implement the **application** layer (orchestration across domains and transports — no permanent home for raw I/O libraries).
|
|
52
|
+
- Wire everything together (replace 501 with real logic).
|
|
53
|
+
- Wait for the user to review and accept before proceeding.
|
|
54
|
+
|
|
55
|
+
## 5. Documentation (required)
|
|
56
|
+
|
|
57
|
+
After the user accepts the service and application layer:
|
|
58
|
+
|
|
59
|
+
Update **every** doc surface the feature touches. Use the **write-flow-doc** skill for new or changed HTTP behavior. Follow **`no-code-in-docs.mdc`** and **`flow-documentation.mdc`**.
|
|
60
|
+
|
|
61
|
+
| Change | Update |
|
|
62
|
+
| ------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
63
|
+
| New or changed HTTP endpoint | Flow doc under `docs/domains/{domain}/`; add **`Current Implementation Status`** |
|
|
64
|
+
| New or changed route in the index | `docs/library/api.md` (endpoint list only — see **`api-reference-docs.mdc`**) |
|
|
65
|
+
| New flow or design doc file | `docs/domains/README.md` (and `docs/design/README.md` if cross-cutting) |
|
|
66
|
+
| New cross-cutting mechanism | `docs/design/*-design.md` |
|
|
67
|
+
| New or changed worker schedule or ARQ task | `docs/library/workers.md`; link from domain flow or design doc |
|
|
68
|
+
| Domain scope, tables, or invariants changed | `core_framework/domains/{domain}/README.md` |
|
|
69
|
+
| New audit trigger | `docs/platform/database-triggers.md` |
|
|
70
|
+
| Settings / config the host must wire | `docs/library/package-api.md` and/or **add-config** skill targets |
|
|
71
|
+
| Accepted policy or ADR | `docs/platform/architecture-decisions.md` when the feature locks a new decision |
|
|
72
|
+
|
|
73
|
+
**Do not** mark a feature done while any row above still applies and is missing an update.
|
|
74
|
+
|
|
75
|
+
Wait for the user to review and accept documentation before proceeding (or before declaring the feature complete when tests are skipped).
|
|
76
|
+
|
|
77
|
+
## 6. Tests (when requested)
|
|
78
|
+
|
|
79
|
+
After documentation is accepted:
|
|
80
|
+
|
|
81
|
+
- Add or update integration tests when the user asks for tests in this task.
|
|
82
|
+
- If the user did not ask for tests, the feature is **done** after step 5 is accepted.
|
|
83
|
+
|
|
84
|
+
## Flexibility
|
|
85
|
+
|
|
86
|
+
If the feature has no database change (e.g., pure logic, workers) or no API layer (e.g., background workers, internal jobs), skip the irrelevant steps and follow the remaining order. **Documentation (step 5) still applies** whenever behavior ships.
|
|
87
|
+
|
|
88
|
+
## Checklist (verify before every response)
|
|
89
|
+
|
|
90
|
+
- [ ] Did I implement only one step?
|
|
91
|
+
- [ ] Did I stop and wait for review?
|
|
92
|
+
- [ ] Did I skip ahead to other layers? (If yes, roll back and redo.)
|
|
93
|
+
- [ ] If this step finishes the feature: is **all** required documentation updated? (If no, the feature is **not** done.)
|
|
@@ -12,7 +12,7 @@ Consuming applications embed **core-framework** as a dependency.
|
|
|
12
12
|
When changing **core-framework** code, migrations, tests, or **`docs/`**:
|
|
13
13
|
|
|
14
14
|
- **Do not** edit host repos in the same task.
|
|
15
|
-
- **Do not** port **`docs/platform/`**, **`docs/deployments/`**, **`.cursor/rules/`**, or **`.cursor/skills/`** to hosts proactively.
|
|
15
|
+
- **Do not** port **`docs/platform/`**, **`docs/design/`**, **`docs/deployments/`**, **`.cursor/rules/`**, or **`.cursor/skills/`** to hosts proactively.
|
|
16
16
|
- Mention that host sync is **deferred until the host bumps its core-framework version** (unless the user asked otherwise).
|
|
17
17
|
|
|
18
18
|
## When host changes are allowed
|
|
@@ -35,7 +35,7 @@ Diff by **basename** against the host's **`.cursor/`** and applicable **`docs/`*
|
|
|
35
35
|
|
|
36
36
|
**Core `docs/` must stay host-agnostic** — no references to specific consuming applications.
|
|
37
37
|
|
|
38
|
-
| Core path
|
|
39
|
-
|
|
|
40
|
-
| **`docs/platform/`**, **`docs/deployments/`** | Sync on dependency upgrade or explicit sync request |
|
|
41
|
-
| **`docs/domains/`**, **`docs/library/`**
|
|
38
|
+
| Core path | Host behavior |
|
|
39
|
+
| ----------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
40
|
+
| **`docs/platform/`**, **`docs/design/`**, **`docs/deployments/`** | Sync on dependency upgrade or explicit sync request |
|
|
41
|
+
| **`docs/domains/`**, **`docs/library/`** | Canonical in core only — do not mirror product trees to hosts |
|
|
@@ -74,5 +74,6 @@ Concise, active voice, present tense, client perspective. Horizontal rules betwe
|
|
|
74
74
|
|
|
75
75
|
- [ ] Endpoint list at top; each endpoint has Goal, Preconditions, Flow, Errors
|
|
76
76
|
- [ ] No implementation identifiers or code snippets
|
|
77
|
-
- [ ] Links to **`docs/platform/architecture-decisions.md`** when referencing
|
|
77
|
+
- [ ] Links to **`docs/platform/architecture-decisions.md`** when referencing accepted policy or ADR decisions
|
|
78
|
+
- [ ] Links to **`docs/design/`** when referencing cross-cutting mechanisms (outbox, events ingest, idempotency, analytics rollup, etc.)
|
|
78
79
|
```
|
|
@@ -4,13 +4,45 @@ Notable changes to **core-framework** (import **`core_framework`**). Format foll
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.3.2] - 2026-07-05
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **UTM attribution ingest:** `POST /events` with `type: "utm_data"` records `analytics.utm_events` (`utm` and `request_context` jsonb; optional `user_id`). UTM keys are sent as `events[]` items (`subject_type` = field name, `subject` = value). See **`docs/domains/events/events.md`** and **`docs/design/utm-events-design.md`**.
|
|
12
|
+
- **Signup telemetry:** `POST /events` with `type: "signup"` records `user_signup_events` (one row per user, `request_context` jsonb) after `POST /auth/register`. See **`docs/domains/events/events.md`**.
|
|
13
|
+
- **Request context parsing:** `application/shared/request_context.py` normalizes `User-Agent`, `client-referrer` (`referrer_url`, `referrer_host`), and `Cf-Ipcountry` for events ingest.
|
|
14
|
+
|
|
15
|
+
### Breaking
|
|
16
|
+
|
|
17
|
+
- **Breaking (clients — events ingest):** Traffic source for request context uses the **`client-referrer`** header instead of browser **`Referer`**. Clients should send an explicit referrer URL (for example captured from `document.referrer` on first load).
|
|
18
|
+
- **Breaking (clients — events ingest):** Rename event type **`view`** → **`impression`** on `POST /events`. Clients and hosts must send `type: "impression"` for subject-impression batches (post views, calculator opens, etc.). Core enum: **`EventType.IMPRESSION`** (replaces **`EventType.VIEW`**). Host handlers registered on `"view"` must re-register on **`"impression"`** (for example Henry calculator view ingest).
|
|
19
|
+
- **Breaking (hosts — imports):** Rename **`EVENT_TOKEN_MAX_AGE_VIEW_SECONDS`** → **`EVENT_TOKEN_MAX_AGE_SECONDS`** in **`core_framework.application.events.event_token`** (behavior unchanged; token max-age remains 600s for all event types).
|
|
20
|
+
|
|
21
|
+
### Migration
|
|
22
|
+
|
|
23
|
+
- **`analytics` schema:** **`585105e1149e_analytics_add_utm_events`** — `utm_events` table. Run **`cf-alembic`** when upgrading.
|
|
24
|
+
- **`user` schema:** **`44df70a45ba6_user_add_signup_events`** — `user_signup_events` table (`user_id` unique, `request_context` jsonb). Run **`cf-alembic`** when upgrading.
|
|
25
|
+
|
|
26
|
+
## [2.3.1] - 2026-07-03
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **Partial admin user deletions:** `tombstone_authored_posts_only` and `tombstone_authored_comments_only` on `POST /admin/users/{user_id}/deletions` — moderator tombstone of authored posts or comments while keeping the account. See **`docs/domains/deletion/users.md`**.
|
|
31
|
+
- **`docs/testing.md`** — usage-focused pytest plugin guide for host adopters (wiring, fixtures, tiers, troubleshooting). Design reference remains **`docs/library/testing-plugin-design.md`**.
|
|
32
|
+
- **Post/comment attachment integration tests:** v1.1 create/edit coverage — best-effort attach (unknown or already-linked assets), attach before worker publish, edit-budget enforcement, clear-all patch, and tombstone finals cleanup. See **`tests/integration/api/posts/post_attachments_integration_test.py`** and **`tests/integration/api/comments/comment_attachments_integration_test.py`**.
|
|
33
|
+
- **Analytics rollup:** `retrieve_daily_fact_kind_totals_for_range` on `AnalyticsService` and `retrieve_analytics_kind_totals_for_range` in `application/analytics/read_service` — sum `daily_fact` counts per `kind` over an inclusive UTC day range (for host catalog and similar reads).
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- **Admin outbox schemas:** rename response field `schema` to `producing_schema` with JSON alias `schema` so Pydantic no longer warns about shadowing `BaseModel.schema`.
|
|
38
|
+
|
|
7
39
|
## [2.3.0] - 2026-07-03
|
|
8
40
|
|
|
9
41
|
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
42
|
|
|
11
43
|
### Added
|
|
12
44
|
|
|
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/
|
|
45
|
+
- **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/design/analytics-rollup-design.md`**.
|
|
14
46
|
- **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
47
|
- **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
48
|
- **Analytics rollup (Phase 4):** `comment` / `created`, `user` / `registered`, and `post` / `viewed` activity sources with domain UTC-day count accessors and registry registration.
|
|
@@ -30,7 +62,7 @@ Transactional outbox (block, follow, restriction), post views retention, and inc
|
|
|
30
62
|
### Added
|
|
31
63
|
|
|
32
64
|
- **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/
|
|
65
|
+
- **Transactional outbox (block pilot):** `user.event_outbox`, worker poller/handler for **`user.block.relationship_changed`**, durable post/comment/notification block mirror fanout. See **`docs/design/event-outbox-design.md`**.
|
|
34
66
|
- **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
67
|
- **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
68
|
|
|
@@ -75,7 +107,7 @@ First production-ready **2.x** release. Hosts must run **`cf-alembic`**, configu
|
|
|
75
107
|
|
|
76
108
|
- **Post and comment image attachments (v1):** **`POST /attachments`** compose-time ingest; worker encodes and publishes **`thumb`** / **`display`** WebP finals; **`attachment_ids`** on post/comment create and **`PATCH`** diff edit — attach is **best-effort per added asset** on create **and** edit (failing assets are skipped and burned; the post/comment write still succeeds and mention notifications proceed; malformed sets — duplicates or over-cap — fail request validation with **`422`**, with the same guards re-checked in the post/comment domain services); public and admin read **`attachments`** wrapper on post/comment responses; staging housekeeping sweeps; admin hard delete and account removal enqueue attachment finals cleanup. Tables **`post_image_attachments`**, **`comment_image_attachments`**, **`attachment_staging_registry`**. Config **`[attachment]`** and deploy vars **`ATTACHMENT_*`**. See **`docs/domains/media/post-comment-attachments-design.md`**.
|
|
77
109
|
|
|
78
|
-
- **HTTP create idempotency (v1):** Optional **`Idempotency-Key`** on **`POST /posts`**, **`POST /comments`**, and **`POST /comments/{comment_id}/replies`**. At most one committed row per **`(author_id, key, operation)`**; replays return **`201`** with empty body and skip mention notifications, attachment attach, and stats-dirty side effects. See **`docs/
|
|
110
|
+
- **HTTP create idempotency (v1):** Optional **`Idempotency-Key`** on **`POST /posts`**, **`POST /comments`**, and **`POST /comments/{comment_id}/replies`**. At most one committed row per **`(author_id, key, operation)`**; replays return **`201`** with empty body and skip mention notifications, attachment attach, and stats-dirty side effects. See **`docs/design/create-idempotency-design.md`**.
|
|
79
111
|
|
|
80
112
|
- **Request profiling (opt-in):** **`PROFILING_ENABLED`** and **`PROFILING_KEY`** — pyinstrument middleware gated by **`Profiling-Key`** header.
|
|
81
113
|
|
|
@@ -136,7 +168,7 @@ Run **`uv run cf-alembic`** (or host **`cf-alembic`**) before deploying **2.0.0*
|
|
|
136
168
|
|
|
137
169
|
### Documentation
|
|
138
170
|
|
|
139
|
-
- **`docs/platform/client-error-visibility.md`**, **`docs/
|
|
171
|
+
- **`docs/platform/client-error-visibility.md`**, **`docs/design/create-idempotency-design.md`**, **`docs/deployments/edge-upload-limits.md`**, **`docs/deployments/deploy-playbook.md`**, deletion refactor docs under **`docs/domains/deletion/`**.
|
|
140
172
|
|
|
141
173
|
## [1.8.1] - 2026-06-07
|
|
142
174
|
|
|
@@ -400,4 +432,7 @@ First **SemVer-stable** release per **`docs/library/package-api.md`**.
|
|
|
400
432
|
[2.0.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.8.1...v2.0.0
|
|
401
433
|
[2.1.0]: https://github.com/NepNepFFXIV/core-framework/compare/v2.0.0...v2.1.0
|
|
402
434
|
[2.2.0]: https://github.com/NepNepFFXIV/core-framework/compare/v2.1.0...v2.2.0
|
|
403
|
-
[
|
|
435
|
+
[2.3.0]: https://github.com/NepNepFFXIV/core-framework/compare/v2.2.0...v2.3.0
|
|
436
|
+
[2.3.1]: https://github.com/NepNepFFXIV/core-framework/compare/v2.3.0...v2.3.1
|
|
437
|
+
[2.3.2]: https://github.com/NepNepFFXIV/core-framework/compare/v2.3.1...v2.3.2
|
|
438
|
+
[unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v2.3.2...HEAD
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-framework
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
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
|
|
@@ -10,7 +10,6 @@ Requires-Python: >=3.14
|
|
|
10
10
|
Requires-Dist: aiofiles>=25.1.0
|
|
11
11
|
Requires-Dist: alembic>=1.18.4
|
|
12
12
|
Requires-Dist: arq>=0.28.0
|
|
13
|
-
Requires-Dist: async-lru>=2.1.0
|
|
14
13
|
Requires-Dist: asyncer>=0.0.14
|
|
15
14
|
Requires-Dist: asyncpg>=0.31.0
|
|
16
15
|
Requires-Dist: asyncssh>=2.23.0
|
|
@@ -62,6 +61,8 @@ make run
|
|
|
62
61
|
|
|
63
62
|
For **`make test`**, use the Firebase CLI (Auth emulator). Stop DB/Redis: **`docker compose -f docker-compose.dev.yaml down`**.
|
|
64
63
|
|
|
64
|
+
**Pre-commit:** `uv sync --dev` installs **pre-commit**. Enable hooks with **`uv run pre-commit install`** (ruff, mdformat, uv-lock, and other checks in **`.pre-commit-config.yaml`**).
|
|
65
|
+
|
|
65
66
|
## Hosts
|
|
66
67
|
|
|
67
68
|
In your host app, load **`Settings`** or a **`Settings`** subclass from **`core_framework.core.settings`**, pass it to **`init_app`** from **`core_framework.main`**, then wire host-specific dependencies, exception handlers, and routers.
|
|
@@ -23,6 +23,8 @@ make run
|
|
|
23
23
|
|
|
24
24
|
For **`make test`**, use the Firebase CLI (Auth emulator). Stop DB/Redis: **`docker compose -f docker-compose.dev.yaml down`**.
|
|
25
25
|
|
|
26
|
+
**Pre-commit:** `uv sync --dev` installs **pre-commit**. Enable hooks with **`uv run pre-commit install`** (ruff, mdformat, uv-lock, and other checks in **`.pre-commit-config.yaml`**).
|
|
27
|
+
|
|
26
28
|
## Hosts
|
|
27
29
|
|
|
28
30
|
In your host app, load **`Settings`** or a **`Settings`** subclass from **`core_framework.core.settings`**, pass it to **`init_app`** from **`core_framework.main`**, then wire host-specific dependencies, exception handlers, and routers.
|
core_framework-2.3.2/alembic/analytics/alembic/versions/585105e1149e_analytics_add_utm_events.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""analytics utm_events for client UTM attribution ingest
|
|
2
|
+
|
|
3
|
+
Revision ID: 585105e1149e
|
|
4
|
+
Revises: 7f2a9c1d4e8b
|
|
5
|
+
Create Date: 2026-07-05 12:00:00.000000
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
from alembic import op
|
|
12
|
+
|
|
13
|
+
revision: str = "585105e1149e"
|
|
14
|
+
down_revision: str | Sequence[str] | None = "7f2a9c1d4e8b"
|
|
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 utm_events (
|
|
22
|
+
id bigint generated always as identity primary key,
|
|
23
|
+
created_at timestamptz not null default now(),
|
|
24
|
+
token varchar(256) not null,
|
|
25
|
+
user_id varchar(128),
|
|
26
|
+
utm jsonb not null default '{}'::jsonb,
|
|
27
|
+
request_context jsonb not null default '{}'::jsonb
|
|
28
|
+
);
|
|
29
|
+
""")
|
|
30
|
+
op.execute("""
|
|
31
|
+
create index if not exists idx_utm_events_created_at
|
|
32
|
+
on utm_events (created_at);
|
|
33
|
+
""")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def downgrade() -> None:
|
|
37
|
+
op.execute("drop index if exists idx_utm_events_created_at;")
|
|
38
|
+
op.execute("drop table if exists utm_events;")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""add user_signup_events for registration telemetry
|
|
2
|
+
|
|
3
|
+
Revision ID: 44df70a45ba6
|
|
4
|
+
Revises: 9d9611e47ce2
|
|
5
|
+
Create Date: 2026-07-04 12:00:00.000000
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
from alembic import op
|
|
12
|
+
|
|
13
|
+
revision: str = "44df70a45ba6"
|
|
14
|
+
down_revision: str | Sequence[str] | None = "9d9611e47ce2"
|
|
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 user_signup_events (
|
|
22
|
+
id bigint generated always as identity primary key,
|
|
23
|
+
user_id varchar(128) not null references users(user_id) on delete cascade,
|
|
24
|
+
created_at timestamptz not null default now(),
|
|
25
|
+
request_context jsonb not null default '{}'::jsonb,
|
|
26
|
+
constraint user_signup_events_user_id_unique unique (user_id)
|
|
27
|
+
);
|
|
28
|
+
""")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def downgrade() -> None:
|
|
32
|
+
op.execute("drop table if exists user_signup_events")
|
|
@@ -2,7 +2,7 @@ from datetime import datetime
|
|
|
2
2
|
from typing import Annotated, Any
|
|
3
3
|
|
|
4
4
|
from fastapi import Path, Query
|
|
5
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
6
|
|
|
7
7
|
OutboxRowId = Annotated[
|
|
8
8
|
int,
|
|
@@ -34,7 +34,9 @@ class OutboxOldestPendingRow(BaseModel):
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
class OutboxSummaryResponse(BaseModel):
|
|
37
|
-
|
|
37
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
38
|
+
|
|
39
|
+
producing_schema: str = Field(alias="schema")
|
|
38
40
|
total_pending: int
|
|
39
41
|
total_dead: int
|
|
40
42
|
pending_by_event: list[OutboxEventCountItem]
|
|
@@ -43,8 +45,10 @@ class OutboxSummaryResponse(BaseModel):
|
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
class OutboxDeadRowResponse(BaseModel):
|
|
48
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
49
|
+
|
|
46
50
|
id: int
|
|
47
|
-
|
|
51
|
+
producing_schema: str = Field(alias="schema")
|
|
48
52
|
event_name: str
|
|
49
53
|
aggregate_type: str
|
|
50
54
|
aggregate_id: str
|
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from pydantic import BaseModel, EmailStr, Field
|
|
1
|
+
from pydantic import BaseModel, EmailStr
|
|
4
2
|
|
|
5
3
|
from core_framework.api.users.shared.schemas import UserID, Username
|
|
6
4
|
|
|
7
|
-
CountryCode = Annotated[
|
|
8
|
-
str,
|
|
9
|
-
Field(
|
|
10
|
-
description="ISO 3166-1 alpha-2 country code",
|
|
11
|
-
pattern="^[A-Z]{2}$",
|
|
12
|
-
examples=["US", "GB"],
|
|
13
|
-
),
|
|
14
|
-
]
|
|
15
|
-
|
|
16
5
|
|
|
17
6
|
class CreatedUserResponse(BaseModel):
|
|
18
7
|
user_id: UserID
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
from typing import Annotated
|
|
2
2
|
|
|
3
|
-
from async_lru import alru_cache
|
|
4
3
|
from fastapi import Depends, Header, Query, Security
|
|
5
4
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
6
5
|
from pydantic import ValidationError
|
|
7
6
|
|
|
8
7
|
import core_framework.domains.auth.dependencies as auth_deps
|
|
9
|
-
from core_framework.api.auth.schemas import
|
|
8
|
+
from core_framework.api.auth.schemas import FirebaseUser
|
|
10
9
|
from core_framework.api.users.shared.schemas import UserID, parse_user_id
|
|
11
10
|
from core_framework.application.auth.access_service import get_user_detail
|
|
12
11
|
from core_framework.application.auth.models import UserDetail
|
|
13
12
|
from core_framework.application.shared.exceptions import BadRequestException, ForbiddenException, UnauthorizedException
|
|
14
|
-
from core_framework.application.shared.
|
|
13
|
+
from core_framework.application.shared.request_context import (
|
|
14
|
+
parse_cf_ipcountry,
|
|
15
|
+
parse_client_referrer,
|
|
16
|
+
parse_user_agent,
|
|
17
|
+
)
|
|
15
18
|
from core_framework.application.users.admin_service import retrieve_admin_user_ids
|
|
16
19
|
from core_framework.core.context import user_id
|
|
17
20
|
from core_framework.domains.auth import VerifiedTokenClaims
|
|
@@ -21,11 +24,6 @@ from core_framework.domains.user import UserIdentity
|
|
|
21
24
|
bearer_scheme = HTTPBearer(auto_error=False)
|
|
22
25
|
|
|
23
26
|
|
|
24
|
-
@alru_cache(maxsize=5000, ttl=300) # ~10MB per worker, 5 minutes TTL
|
|
25
|
-
async def _verify_id_token(token: str) -> VerifiedTokenClaims | None:
|
|
26
|
-
return await auth_deps.auth_service.verify_id_token(token=token)
|
|
27
|
-
|
|
28
|
-
|
|
29
27
|
def _build_firebase_user_from_claims(claims: VerifiedTokenClaims) -> FirebaseUser | None:
|
|
30
28
|
try:
|
|
31
29
|
return FirebaseUser(
|
|
@@ -54,7 +52,7 @@ async def _get_firebase_user(token: Annotated[str | None, Depends(_get_bearer_to
|
|
|
54
52
|
if not token:
|
|
55
53
|
return None
|
|
56
54
|
|
|
57
|
-
claims = await
|
|
55
|
+
claims = await auth_deps.auth_service.verify_id_token(token=token)
|
|
58
56
|
if claims is None:
|
|
59
57
|
return None
|
|
60
58
|
|
|
@@ -106,18 +104,18 @@ async def check_not_banned(user_detail: Annotated[UserDetail, Depends(_get_user_
|
|
|
106
104
|
async def _get_request_context(
|
|
107
105
|
firebase_user: Annotated[FirebaseUser | None, Depends(_get_firebase_user)],
|
|
108
106
|
user_agent: Annotated[str | None, Header()] = None,
|
|
109
|
-
cf_ipcountry: Annotated[
|
|
107
|
+
cf_ipcountry: Annotated[str | None, Header()] = None,
|
|
108
|
+
client_referrer: Annotated[str | None, Header()] = None,
|
|
110
109
|
) -> dict[str, str]:
|
|
111
110
|
metadata: dict[str, str] = {}
|
|
112
111
|
|
|
113
112
|
if firebase_user:
|
|
114
113
|
metadata["viewer_id"] = firebase_user.user_id
|
|
115
114
|
metadata["provider"] = firebase_user.sign_in_provider
|
|
116
|
-
if cf_ipcountry:
|
|
117
|
-
metadata["country_code"] = cf_ipcountry
|
|
118
115
|
|
|
119
|
-
|
|
120
|
-
metadata.update(
|
|
116
|
+
metadata.update(parse_cf_ipcountry(cf_ipcountry=cf_ipcountry))
|
|
117
|
+
metadata.update(parse_client_referrer(client_referrer=client_referrer))
|
|
118
|
+
metadata.update(parse_user_agent(user_agent=user_agent))
|
|
121
119
|
|
|
122
120
|
return {k: v for k, v in metadata.items() if v is not None}
|
|
123
121
|
|
{core_framework-2.3.0 → core_framework-2.3.2}/core_framework/application/analytics/read_service.py
RENAMED
|
@@ -27,6 +27,31 @@ async def retrieve_analytics_daily_range(
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
async def retrieve_analytics_kind_totals_for_range(
|
|
31
|
+
*,
|
|
32
|
+
subject_type: str,
|
|
33
|
+
activity_type: str,
|
|
34
|
+
from_day: date,
|
|
35
|
+
to_day: date,
|
|
36
|
+
kind: str | None = None,
|
|
37
|
+
) -> dict[str, Any]:
|
|
38
|
+
"""Return per-kind rollup totals summed over an inclusive UTC day range."""
|
|
39
|
+
totals = await analytics_deps.analytics_service.retrieve_daily_fact_kind_totals_for_range(
|
|
40
|
+
subject_type=subject_type,
|
|
41
|
+
activity_type=activity_type,
|
|
42
|
+
from_day=from_day,
|
|
43
|
+
to_day=to_day,
|
|
44
|
+
kind=kind,
|
|
45
|
+
)
|
|
46
|
+
return {
|
|
47
|
+
"subject_type": subject_type,
|
|
48
|
+
"activity_type": activity_type,
|
|
49
|
+
"from_day": from_day,
|
|
50
|
+
"to_day": to_day,
|
|
51
|
+
"items": [{"kind": total.kind, "count": total.count} for total in totals],
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
30
55
|
async def retrieve_analytics_daily_day(
|
|
31
56
|
*,
|
|
32
57
|
subject_type: str,
|