core-framework 1.4.0__tar.gz → 1.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- core_framework-1.6.0/.cursor/rules/code-review-output.mdc +31 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/domain-imports.mdc +17 -1
- core_framework-1.6.0/.cursor/rules/domain-services-and-adapters.mdc +47 -0
- core_framework-1.6.0/.cursor/rules/exception-handlers.mdc +33 -0
- core_framework-1.6.0/.cursor/rules/henry-cursor-rules-sync.mdc +52 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/implementation-workflow.mdc +5 -5
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/layer-boundaries.mdc +4 -2
- core_framework-1.6.0/.cursor/rules/structured-logging.mdc +35 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/skills/add-domain/SKILL.md +2 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/skills/code-review/SKILL.md +10 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/.github/workflows/_deploy.yml +50 -3
- {core_framework-1.4.0 → core_framework-1.6.0}/.gitignore +8 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/CHANGELOG.md +43 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/PKG-INFO +4 -1
- core_framework-1.6.0/alembic/media/alembic/env.py +72 -0
- core_framework-1.6.0/alembic/media/alembic/versions/v1_media_init_baseline.py +45 -0
- core_framework-1.6.0/alembic/notification/alembic.ini +52 -0
- core_framework-1.6.0/alembic/user/alembic/README +1 -0
- core_framework-1.6.0/alembic/user/alembic/script.py.mako +28 -0
- core_framework-1.6.0/alembic/user/alembic/versions/user_add_avatar_ingest_sequences.py +34 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/config.toml +15 -3
- {core_framework-1.4.0 → core_framework-1.6.0}/config.toml.template +14 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/users/schemas.py +0 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/dependencies.py +11 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/posts/authenticated/schemas.py +3 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/posts/public/router.py +11 -3
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/posts/schemas.py +0 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/authenticated/router.py +55 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/authenticated/schemas.py +7 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/mappers.py +0 -5
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/bootstrap.py +4 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/comments/public_service.py +91 -52
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/events/event_service.py +2 -2
- core_framework-1.6.0/core_framework/application/media/README.md +11 -0
- core_framework-1.6.0/core_framework/application/media/avatar_service.py +148 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/moderation/appeal_service.py +10 -15
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/moderation/user_service.py +41 -38
- core_framework-1.6.0/core_framework/application/shared/enums.py +21 -0
- core_framework-1.6.0/core_framework/application/shared/worker_jobs.py +17 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/users/admin_service.py +4 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/users/authenticated_service.py +2 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/bundled_alembic.py +1 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/exception_handlers/comment.py +10 -25
- core_framework-1.6.0/core_framework/core/exception_handlers/common.py +19 -0
- core_framework-1.6.0/core_framework/core/exception_handlers/media.py +64 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/exception_handlers/moderation.py +10 -30
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/exception_handlers/notification.py +5 -5
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/exception_handlers/post.py +7 -13
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/exception_handlers/setup.py +8 -24
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/exception_handlers/user.py +8 -20
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/redis.py +5 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/runtime.py +3 -3
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/settings.py +68 -11
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/comment/dependencies.py +2 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/comment/models.py +2 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/comment/repository.py +10 -0
- core_framework-1.6.0/core_framework/domains/media/README.md +25 -0
- core_framework-1.6.0/core_framework/domains/media/__init__.py +30 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/components/finals_publisher.py +16 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/components/staging.py +51 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/components/variant_encoder.py +15 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/constants.py +26 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/exceptions.py +26 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/models.py +24 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/ports/finals_publisher.py +7 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/ports/staging.py +10 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/ports/variant_encoder.py +7 -0
- core_framework-1.6.0/core_framework/domains/media/avatar/service.py +162 -0
- core_framework-1.6.0/core_framework/domains/media/dependencies.py +82 -0
- core_framework-1.6.0/core_framework/domains/media/shared/exceptions.py +6 -0
- core_framework-1.6.0/core_framework/domains/media/shared/repository.py +179 -0
- core_framework-1.6.0/core_framework/domains/media/shared/service.py +6 -0
- core_framework-1.6.0/core_framework/domains/media/shared/utils.py +21 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/moderation/dependencies.py +2 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/moderation/service.py +4 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/notification/dependencies.py +2 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/notification/repository.py +8 -6
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/notification/service.py +2 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/post/constants.py +2 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/post/dependencies.py +2 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/post/repository.py +97 -8
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/post/service.py +18 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/__init__.py +2 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/dependencies.py +2 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/models.py +6 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/repository.py +66 -7
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/service.py +23 -0
- core_framework-1.6.0/core_framework/infrastructure/media/local_staging_adapter.py +61 -0
- core_framework-1.6.0/core_framework/infrastructure/media/pillow_variant_encoder_adapter.py +45 -0
- core_framework-1.6.0/core_framework/infrastructure/media/ssh_finals_publisher_adapter.py +101 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/main.py +3 -3
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/arq.py +2 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/auth.py +37 -0
- core_framework-1.6.0/core_framework/testing/media.py +81 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/main.py +4 -3
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/schedules/__init__.py +10 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +8 -5
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/schedules/schedule_aggregate_post_stats.py +6 -5
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/schedules/schedule_aggregate_user_stats.py +6 -5
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/schedules/schedule_expired_account_deletions.py +4 -3
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/schedules/schedule_expired_mute_lifts.py +4 -3
- core_framework-1.6.0/core_framework/worker/schedules/schedule_sweep_stale_avatar_staging.py +14 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/tasks/__init__.py +4 -0
- core_framework-1.6.0/core_framework/worker/tasks/delete_superseded_avatar_finals.py +16 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/tasks/process_account_deletion.py +1 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/tasks/process_aggregate_comment_stats.py +2 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/tasks/process_aggregate_post_stats.py +2 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/tasks/process_aggregate_user_stats.py +2 -2
- core_framework-1.6.0/core_framework/worker/tasks/process_media_asset_variants.py +31 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/tasks/process_mute_lift.py +1 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/docker-compose.yml +4 -0
- core_framework-1.6.0/docs/README.md +22 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/api.md +1 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/architecture.md +13 -5
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/conventions.md +1 -0
- core_framework-1.6.0/docs/deployment/Caddyfile.example +24 -0
- core_framework-1.6.0/docs/deployment/edge-upload-limits.md +59 -0
- core_framework-1.6.0/docs/deployment/image-server-known-hosts.example +5 -0
- core_framework-1.6.0/docs/deployment/image-server-ssh.md +84 -0
- core_framework-1.6.0/docs/domain-services-and-adapters.md +179 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/comments/retrieve_comments.md +4 -2
- core_framework-1.6.0/docs/flows/media/upload_pipeline.md +37 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/notifications/notification_inbox.md +1 -1
- core_framework-1.6.0/docs/flows/posts/post_visibility.md +75 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/account.md +14 -3
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/avatar.md +15 -12
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/change_history.md +1 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/my_posts_and_comments.md +9 -4
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/preferences.md +4 -4
- core_framework-1.6.0/docs/flows/users/profile.md +142 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/follow-system-design.md +5 -3
- core_framework-1.4.0/docs/flows/media/upload_pipeline.md → core_framework-1.6.0/docs/media-upload-pipeline-design.md +87 -82
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/testing-plugin-design.md +8 -9
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/todo.md +2 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/pyproject.toml +4 -1
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/conftest.py +10 -2
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/admin/users/router_test.py +10 -10
- core_framework-1.6.0/tests/integration/api/users/authenticated/avatar_upload_test.py +165 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/users/authenticated/router_test.py +79 -31
- core_framework-1.6.0/tests/integration/api/users/public/__init__.py +0 -0
- core_framework-1.6.0/tests/integration/worker/__init__.py +0 -0
- core_framework-1.6.0/tests/integration/worker/avatar_upload_test.py +167 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/worker/conftest.py +1 -1
- core_framework-1.6.0/tests/unit/__init__.py +0 -0
- core_framework-1.6.0/tests/unit/application/comments/__init__.py +0 -0
- core_framework-1.6.0/tests/unit/domains/__init__.py +0 -0
- core_framework-1.6.0/tests/unit/domains/comment/__init__.py +0 -0
- core_framework-1.6.0/tests/unit/infrastructure/__init__.py +0 -0
- core_framework-1.6.0/tests/unit/infrastructure/media/__init__.py +0 -0
- core_framework-1.6.0/tests/unit/infrastructure/media/ssh_finals_publisher_adapter_test.py +279 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/uv.lock +62 -1
- core_framework-1.4.0/core_framework/application/shared/enums.py +0 -11
- core_framework-1.4.0/core_framework/core/exception_handlers/common.py +0 -5
- core_framework-1.4.0/docs/flows/users/profile.md +0 -89
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/api-layer.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/api-reference-docs.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/api-security.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/api-validation.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/application-layer.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/constants-final.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/database-triggers.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/domain-caller-context.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/domain-input-guards.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/domain-repository-exceptions.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/flow-documentation.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/integration-test-strategy.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/no-code-in-docs.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/no-docstrings.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/postgres-config-conventions.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/repository-read-consistency.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/strong-read-opt-in.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/rules/tech-stack.mdc +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/skills/add-config/SKILL.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.cursor/skills/recommend-features/SKILL.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.dockerignore +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.github/workflows/dev-ci-cd.yaml +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.github/workflows/manual-deployment.yaml +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.github/workflows/publish-pypi.yml +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.github/workflows/test.yaml +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.pre-commit-config.yaml +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/.python-version +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/LICENSE +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/README.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/comment/alembic/README +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/comment/alembic/env.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/comment/alembic/script.py.mako +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/comment/alembic/versions/v1_comment_init_baseline.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/comment/alembic.ini +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/extension/alembic/README +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/extension/alembic/env.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/extension/alembic/script.py.mako +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/extension/alembic/versions/v1_ext_init_baseline.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/extension/alembic.ini +0 -0
- {core_framework-1.4.0/alembic/moderation → core_framework-1.6.0/alembic/media}/alembic/README +0 -0
- {core_framework-1.4.0/alembic/moderation → core_framework-1.6.0/alembic/media}/alembic/script.py.mako +0 -0
- {core_framework-1.4.0/alembic/notification → core_framework-1.6.0/alembic/media}/alembic.ini +0 -0
- {core_framework-1.4.0/alembic/notification → core_framework-1.6.0/alembic/moderation}/alembic/README +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/moderation/alembic/env.py +0 -0
- {core_framework-1.4.0/alembic/notification → core_framework-1.6.0/alembic/moderation}/alembic/script.py.mako +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/moderation/alembic.ini +0 -0
- {core_framework-1.4.0/alembic/post → core_framework-1.6.0/alembic/notification}/alembic/README +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/notification/alembic/env.py +0 -0
- {core_framework-1.4.0/alembic/post → core_framework-1.6.0/alembic/notification}/alembic/script.py.mako +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/notification/alembic/versions/v1_notif_init_baseline.py +0 -0
- {core_framework-1.4.0/alembic/user → core_framework-1.6.0/alembic/post}/alembic/README +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/post/alembic/env.py +0 -0
- {core_framework-1.4.0/alembic/user → core_framework-1.6.0/alembic/post}/alembic/script.py.mako +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/post/alembic/versions/v1_post_init_baseline.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/post/alembic.ini +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/user/alembic/env.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/user/alembic/versions/user_add_date_of_birth_to_profiles.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/user/alembic/versions/v1_user_init_baseline.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/alembic/user/alembic.ini +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/comments/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/comments/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/moderation/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/moderation/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/moderation/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/posts/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/posts/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/users/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/admin/users/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/auth/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/auth/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/auth/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/comments/authenticated/mappers.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/comments/authenticated/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/comments/authenticated/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/comments/public/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/comments/public/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/comments/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/comments/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/constants.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/events/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/events/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/notifications/authenticated/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/notifications/authenticated/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/notifications/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/posts/authenticated/mappers.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/posts/authenticated/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/posts/public/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/posts/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/system/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/system/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/authenticated/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/authenticated/mappers.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/public/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/public/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/public/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/router.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/api/users/shared/schemas.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/auth/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/auth/access_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/auth/auth_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/auth/models.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/comments/admin_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/comments/aggregation_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/comments/authenticated_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/events/README.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/events/event_token.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/events/models.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/moderation/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/moderation/moderator_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/moderation/report_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/moderation/scheduled_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/notifications/README.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/notifications/enums.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/notifications/inbox_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/notifications/mute_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/notifications/notification_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/posts/admin_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/posts/aggregation_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/posts/authenticated_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/posts/public_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/shared/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/shared/exceptions.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/shared/user_agent.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/users/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/users/aggregation_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/users/public_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/application/users/scheduled_service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/asgi.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/constants.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/cache.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/context.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/database.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/exception_handlers/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/firebase.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/http_client.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/logging.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/middleware.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/observability.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/core/pagination.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/comment/README.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/comment/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/comment/constants.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/comment/enums.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/comment/exceptions.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/comment/service.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/exceptions.py +0 -0
- {core_framework-1.4.0/core_framework/worker → core_framework-1.6.0/core_framework/domains/media/avatar}/__init__.py +0 -0
- {core_framework-1.4.0/tests → core_framework-1.6.0/core_framework/domains/media/avatar/components}/__init__.py +0 -0
- {core_framework-1.4.0/tests/integration → core_framework-1.6.0/core_framework/domains/media/avatar/ports}/__init__.py +0 -0
- {core_framework-1.4.0/tests/integration/api → core_framework-1.6.0/core_framework/domains/media/shared}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/moderation/README.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/moderation/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/moderation/enums.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/moderation/exceptions.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/moderation/models.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/moderation/repository.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/notification/README.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/notification/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/notification/enums.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/notification/exceptions.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/notification/models.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/post/README.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/post/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/post/enums.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/post/exceptions.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/post/models.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/README.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/constants.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/enums.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/exceptions.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/user/utils.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/domains/utils.py +0 -0
- {core_framework-1.4.0/tests/integration/api/admin → core_framework-1.6.0/core_framework/infrastructure}/__init__.py +0 -0
- {core_framework-1.4.0/tests/integration/api/admin/comments → core_framework-1.6.0/core_framework/infrastructure/media}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/migrate_cli.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/config.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/containers.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/firebase.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/hookspecs.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/httpx_test_client.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/migrations.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/testing/plugin.py +0 -0
- {core_framework-1.4.0/tests/integration/api/admin/moderation → core_framework-1.6.0/core_framework/worker}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/core_framework/worker/worker_context.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docker-compose.dev.yaml +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/dockerfile +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/architecture-decisions.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/core-framework-migration.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/database-triggers.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/event-outbox-design.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/auth/access_control.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/auth/registration.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/comments/admin_comments.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/comments/comment_report.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/comments/comment_stats_aggregation.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/comments/create_comment.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/comments/delete_comment.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/comments/edit_comment.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/events/events.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/mentions/mentions_in_content.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/moderation/appeals.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/moderation/internal_notes.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/moderation/moderator_actions.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/moderation/reports.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/moderation/restrictions.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/posts/admin_posts.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/posts/author_context.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/posts/hashtag_discovery.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/posts/post_like.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/posts/post_stats_aggregation.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/account_deletion.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/blocks.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/check_username_exists.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/follow.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/flows/users/user_removal.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/package-api.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/docs/patch-update-typed-payload.md +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/firebase_config.example.json +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/makefile +0 -0
- {core_framework-1.4.0/tests/integration/api/admin/posts → core_framework-1.6.0/tests}/__init__.py +0 -0
- {core_framework-1.4.0/tests/integration/api/admin/users → core_framework-1.6.0/tests/integration}/__init__.py +0 -0
- {core_framework-1.4.0/tests/integration/api/auth → core_framework-1.6.0/tests/integration/api}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/_http_helpers.py +0 -0
- {core_framework-1.4.0/tests/integration/api/comments → core_framework-1.6.0/tests/integration/api/admin}/__init__.py +0 -0
- {core_framework-1.4.0/tests/integration/api/comments/public → core_framework-1.6.0/tests/integration/api/admin/comments}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/admin/comments/router_test.py +0 -0
- {core_framework-1.4.0/tests/integration/api/notifications → core_framework-1.6.0/tests/integration/api/admin/moderation}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/admin/moderation/router_test.py +0 -0
- {core_framework-1.4.0/tests/integration/api → core_framework-1.6.0/tests/integration/api/admin}/posts/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/admin/posts/router_test.py +0 -0
- {core_framework-1.4.0/tests/integration/api/posts/public → core_framework-1.6.0/tests/integration/api/admin/users}/__init__.py +0 -0
- {core_framework-1.4.0/tests/integration/api/system → core_framework-1.6.0/tests/integration/api/auth}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/auth/router_test.py +0 -0
- {core_framework-1.4.0/tests/integration/api/users → core_framework-1.6.0/tests/integration/api/comments}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +0 -0
- {core_framework-1.4.0/tests/integration/api/users/authenticated → core_framework-1.6.0/tests/integration/api/comments/public}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/comments/public/router_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/events/router_test.py +0 -0
- {core_framework-1.4.0/tests/integration/api/users/public → core_framework-1.6.0/tests/integration/api/notifications}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/notifications/router_test.py +0 -0
- {core_framework-1.4.0/tests/integration/worker → core_framework-1.6.0/tests/integration/api/posts}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/posts/authenticated/post_writes_integration_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/posts/comment_count_aggregation_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/posts/followers_visibility_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/posts/post_stats_dirty_marking_test.py +0 -0
- {core_framework-1.4.0/tests/unit → core_framework-1.6.0/tests/integration/api/posts/public}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/posts/public/router_test.py +0 -0
- {core_framework-1.4.0/tests/unit/application/comments → core_framework-1.6.0/tests/integration/api/system}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/system/router_test.py +0 -0
- {core_framework-1.4.0/tests/unit/domains → core_framework-1.6.0/tests/integration/api/users}/__init__.py +0 -0
- {core_framework-1.4.0/tests/unit/domains/comment → core_framework-1.6.0/tests/integration/api/users/authenticated}/__init__.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/api/users/public/router_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/worker/account_deletion_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/worker/aggregate_comment_stats_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/worker/aggregate_post_stats_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/worker/aggregate_user_stats_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/worker/mute_lift_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/integration/worker/utils_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/unit/application/events/event_service_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/unit/application/notifications/inbox_service_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/unit/core/bundled_alembic_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/unit/core/migrate_cli_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/unit/core/pagination_test.py +0 -0
- {core_framework-1.4.0 → core_framework-1.6.0}/tests/unit/domains/user/service_test.py +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: When the user asks for a code review or audit, report only issues and improvements—not strengths
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code review output
|
|
7
|
+
|
|
8
|
+
When the user asks for a **review**, **audit**, or similar (e.g. "review my code", "comprehensive review", "what's wrong with"):
|
|
9
|
+
|
|
10
|
+
## Report only
|
|
11
|
+
|
|
12
|
+
- **Bugs** and correctness risks
|
|
13
|
+
- **Wrong** behavior vs design docs, API contracts, or project rules
|
|
14
|
+
- **Improvements** and **optimizations** worth making
|
|
15
|
+
- **Contradictions** between code and documented intent (including doc drift)
|
|
16
|
+
|
|
17
|
+
Prioritize by severity. Include enough context (file, behavior, suggested fix direction) to act on each item.
|
|
18
|
+
|
|
19
|
+
## Do not report
|
|
20
|
+
|
|
21
|
+
- Sections praising what is **solid**, **well done**, or **correctly implemented**
|
|
22
|
+
- **"What's good"** / **"Overall assessment"** summaries that restate strengths
|
|
23
|
+
- Long **checklist pass** narratives ("layering is clean", "DI is wired correctly") unless a listed item is actually broken
|
|
24
|
+
|
|
25
|
+
A one-line **scope** (what was reviewed) is fine. If nothing actionable is found, say so briefly—do not pad with compliments.
|
|
26
|
+
|
|
27
|
+
## Accepted trade-offs
|
|
28
|
+
|
|
29
|
+
Do **not** list documented accepted trade-offs as findings unless the user asked about them or the implementation **violates** the documented trade-off (then report the violation).
|
|
30
|
+
|
|
31
|
+
Follow the **code-review** skill for *how* to review (docs to read, checklists); this rule governs *what to include in the answer*.
|
|
@@ -57,6 +57,15 @@ from core_framework.domains.user.service import UserService # ❌
|
|
|
57
57
|
4. **Consistency across adapters**: API and application layers follow the same domain import contract
|
|
58
58
|
5. **Dependency direction**: Prevents tight coupling to internal implementation details
|
|
59
59
|
|
|
60
|
+
## Domain `__init__.py` export policy
|
|
61
|
+
|
|
62
|
+
Export a symbol from a domain's **`__init__.py`** only when it is consumed **outside** that domain package — for example API, application, infrastructure adapters, exception handlers, or workers (via application services).
|
|
63
|
+
|
|
64
|
+
- **Do export**: exceptions, constants, and models that outer layers or adapters import.
|
|
65
|
+
- **Do not export**: persistence/registry shapes, repository row mappings, and other types used only inside **`domains/<name>/`** (repository, service, components). Import those from submodule paths within the domain.
|
|
66
|
+
|
|
67
|
+
When adding a new model or exception, default to **not** exporting; add to **`__init__.py`** only when an outer layer needs it.
|
|
68
|
+
|
|
60
69
|
## Available Domain Exports
|
|
61
70
|
|
|
62
71
|
### Moderation Domain (`from core_framework.domains.moderation import ...`)
|
|
@@ -72,11 +81,18 @@ from core_framework.domains.user.service import UserService # ❌
|
|
|
72
81
|
- **Service**: Use `user_service` from `dependencies.py`
|
|
73
82
|
- **Internal** (do not import from API layer): `DomainUserNotFoundException` — handled within domain; API uses `None` return values
|
|
74
83
|
|
|
84
|
+
### Media Domain (`from core_framework.domains.media import ...`)
|
|
85
|
+
- **Constants**: `AVATAR_INGEST_MAX_BYTES`, `AVATAR_INGEST_UPLOAD_CHUNK_BYTES`, `AVATAR_FINAL_VARIANT_128`, `AVATAR_FINAL_VARIANT_500`
|
|
86
|
+
- **Models**: `AvatarIngestRegistration`, `AvatarVariantBytes`
|
|
87
|
+
- **Exceptions**: `BaseMediaException`, `AvatarIngestBodyTooLargeException`, `AvatarIngestLeaseConflictException`, `AvatarIngestUnsupportedContentTypeException`, `AvatarStagingBlobNotFoundException`, `AvatarFinalsPublishException`
|
|
88
|
+
- **Service**: Use `avatar_service` / `media_service` from `dependencies.py`
|
|
89
|
+
- **Internal** (not in `__init__.py`): `AvatarStagingRegistryEntry` — repository and `AvatarService` only; application uses service methods, not the registry row type
|
|
90
|
+
|
|
75
91
|
## How to Fix Violations
|
|
76
92
|
|
|
77
93
|
If you need something that's not exported in `__init__.py`:
|
|
78
94
|
|
|
79
|
-
1. **Check if it should be public**: If
|
|
95
|
+
1. **Check if it should be public**: If an outer layer (API, application, infrastructure adapter, exception handler) needs it, add it to the domain's `__init__.py`. If only repository/service code inside the domain needs it, keep it internal.
|
|
80
96
|
2. **Reconsider the design**: If it's internal implementation, maybe the API layer shouldn't access it directly
|
|
81
97
|
3. **Use the service**: Most domain logic should be accessed through `{domain}_service` from `dependencies.py`
|
|
82
98
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Domain bounded contexts may use multiple services and ports; application orchestrates; repositories stay Postgres-only
|
|
3
|
+
globs:
|
|
4
|
+
- core_framework/domains/**/*.py
|
|
5
|
+
- core_framework/application/**/*.py
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Domain Services and Adapters
|
|
9
|
+
|
|
10
|
+
See **`docs/domain-services-and-adapters.md`** for the full model. Apply when adding or changing domain packages and application orchestration.
|
|
11
|
+
|
|
12
|
+
## Application = orchestration
|
|
13
|
+
|
|
14
|
+
- **`core_framework/application/...`** sequences multi-step use cases shared by API and worker (call domain services, enqueue jobs, coordinate cross-domain steps).
|
|
15
|
+
- Do **not** move shared orchestration into API routers or worker handlers when it is reused across transports.
|
|
16
|
+
- Do **not** treat application modules as the long-term home for concrete filesystem/HTTP/SSH clients—use domain ports + components + adapters (below).
|
|
17
|
+
|
|
18
|
+
## Domain ≠ database only
|
|
19
|
+
|
|
20
|
+
- A bounded context owns **business rules and contracts** for all of its capabilities, not only its Postgres schema.
|
|
21
|
+
- **Repositories** remain **Postgres-only** (SQL in repository + row mapping).
|
|
22
|
+
- **Non-SQL I/O** uses **ports**, **components**, and **adapters**—not `*Repository`.
|
|
23
|
+
|
|
24
|
+
## Multiple services per domain
|
|
25
|
+
|
|
26
|
+
- Facade **`*Service`** is wired in `dependencies`; internal **`*Component`** classes wrap ports.
|
|
27
|
+
- Do not mix SQL, `aiofiles`, HTTP, or SSH in one class.
|
|
28
|
+
- Application resolves facade services via **`import ...dependencies as ..._deps`**.
|
|
29
|
+
|
|
30
|
+
## Ports, components, and adapters
|
|
31
|
+
|
|
32
|
+
- **Port** — `*Port` in `domains/<capability>/ports/`; no I/O imports.
|
|
33
|
+
- **Component** — `*Component` in `domains/<capability>/components/`; port + domain rules; composed by facade `*Service`.
|
|
34
|
+
- **Adapter** — `*Adapter` in `infrastructure/<domain>/`; transport prefix when multiple impls.
|
|
35
|
+
- Wire adapters only in `dependencies.py`; application uses facade `*_deps` services.
|
|
36
|
+
|
|
37
|
+
## Prohibited
|
|
38
|
+
|
|
39
|
+
- SQL and filesystem/HTTP/SSH in the same **repository** or god **service** class.
|
|
40
|
+
- Domain imports from `core_framework.api`, `core_framework.worker`, or `core_framework.application`.
|
|
41
|
+
- Infrastructure imports in domain modules except **`dependencies.py`** (existing rule).
|
|
42
|
+
|
|
43
|
+
## Media ingest (v1)
|
|
44
|
+
|
|
45
|
+
- **Layout:** `domains/media/shared/`, `avatar/ports/`, `avatar/components/`, `AvatarService`.
|
|
46
|
+
- **Avatar:** `media_deps.avatar_service` only (not individual components).
|
|
47
|
+
- **Cross-domain:** `avatar_ingest_sequence` / profile `avatar_id` via **user** domain; orchestration in `application/media/avatar_service`.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: FastAPI exception handler registration and typing conventions
|
|
3
|
+
globs:
|
|
4
|
+
- core_framework/core/exception_handlers/**/*.py
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Exception Handlers
|
|
8
|
+
|
|
9
|
+
Domain-specific HTTP exception handlers live under **`core_framework/core/exception_handlers/`**.
|
|
10
|
+
|
|
11
|
+
## Registration
|
|
12
|
+
|
|
13
|
+
- Register handlers with **`register_exception_handler`** from **`core_framework.core.exception_handlers.common`**.
|
|
14
|
+
- Do **not** call **`app.add_exception_handler`** directly from domain handler modules or **`setup.py`**.
|
|
15
|
+
- Starlette’s **`ExceptionHandler`** type is wider than narrowly typed handlers; the single **`ty: ignore[invalid-argument-type]`** belongs in **`common.register_exception_handler`** only.
|
|
16
|
+
|
|
17
|
+
## Handler functions
|
|
18
|
+
|
|
19
|
+
- One async handler per mapped exception type: **`(Request, ExcT) -> JSONResponse`**.
|
|
20
|
+
- Type **`exc`** as the **specific** domain exception (not bare **`Exception`**) so handler bodies stay narrow.
|
|
21
|
+
- Map domain exceptions to HTTP status codes; use **`exc.message`** in **`{"detail": ...}`** for client-facing errors.
|
|
22
|
+
- Register **specific** exceptions before domain **base** catch-alls (e.g. **`BasePostException`** last in that module).
|
|
23
|
+
|
|
24
|
+
## Module layout
|
|
25
|
+
|
|
26
|
+
- One module per domain (e.g. **`post.py`**, **`media.py`**) exporting **`add_*_exception_handlers(app)`**.
|
|
27
|
+
- **`setup.py`** registers cross-cutting handlers, then calls each domain **`add_*_exception_handlers`**.
|
|
28
|
+
- Base domain handlers return **`GENERIC_INTERNAL_SERVER_ERROR_DETAIL`** — not raw exception messages.
|
|
29
|
+
|
|
30
|
+
## Avoid
|
|
31
|
+
|
|
32
|
+
- Per-call-site **`ty: ignore[invalid-argument-type]`** or **`cast(ExceptionHandler, ...)`** on registrations.
|
|
33
|
+
- Business logic or repository calls inside handlers — translate exception → HTTP response only.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: When editing Cursor rules or skills in core-framework, sync applicable changes to henry-backend
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Henry cursor rules sync
|
|
7
|
+
|
|
8
|
+
**Henry backend** embeds **core-framework** and shares most **`.cursor/rules/`** and **`.cursor/skills/`** content. Treat both repos as one rules surface unless the user scopes work to core-framework only.
|
|
9
|
+
|
|
10
|
+
## When this applies
|
|
11
|
+
|
|
12
|
+
Whenever you **add, edit, or remove** a file under **`.cursor/rules/`** or **`.cursor/skills/`** in **this** repository — including new always-on rules, skill workflow updates, and convention changes from feature work (e.g. media/avatar).
|
|
13
|
+
|
|
14
|
+
## Required follow-up
|
|
15
|
+
|
|
16
|
+
1. **Sync to henry-backend** in the **same task** or as the **immediate next step** after the user accepts the core-framework rule change.
|
|
17
|
+
2. Diff by **basename** against **`henry-backend/.cursor/`** (sibling repo or second workspace root).
|
|
18
|
+
3. Mention in your response that henry-backend was synced, or that it still needs sync if you stopped at core-only per user scope.
|
|
19
|
+
|
|
20
|
+
## How to sync
|
|
21
|
+
|
|
22
|
+
- **Shared rules/skills** — port into henry-backend and **adapt** for the host:
|
|
23
|
+
- globs: **`core_framework/**`** → **`src/**`** where host code applies
|
|
24
|
+
- imports: **`core_framework....`** vs **`src....`**
|
|
25
|
+
- strong reads: **`strong-read-opt-in`** → **`host-strong-read-opt-in`**
|
|
26
|
+
- host boundaries: reference **`henry-host-context`**
|
|
27
|
+
- **Do not overwrite** henry-only files:
|
|
28
|
+
- **`.cursor/rules/henry-host-context.mdc`**
|
|
29
|
+
- **`.cursor/rules/host-strong-read-opt-in.mdc`**
|
|
30
|
+
- **`.cursor/skills/add-host-domain/SKILL.md`**
|
|
31
|
+
- **Henry-only equivalents** — if core adds a rule with core-only globs (e.g. **`domain-input-guards`** on **`core_framework/**`**), ensure henry has coverage for **`src/**`** via an existing or new host-scoped rule.
|
|
32
|
+
|
|
33
|
+
## Related docs
|
|
34
|
+
|
|
35
|
+
When a rule encodes a new convention, check whether **`docs/`** in both repos need the same story.
|
|
36
|
+
|
|
37
|
+
**Core `docs/` must stay host-agnostic** — no references to Henry, henry-backend, or other specific consuming applications. Use generic “host application” language only.
|
|
38
|
+
|
|
39
|
+
Sync to henry when core changes:
|
|
40
|
+
|
|
41
|
+
- **`docs/architecture.md`** → henry **`docs/architecture.md`** (Henry preamble + synced body; adapt `docs/flows/…` links in henry to **`docs/core-product-docs.md`**)
|
|
42
|
+
- **`docs/domain-services-and-adapters.md`** → update henry host summary; core remains canonical
|
|
43
|
+
- **`docs/conventions.md`**, **`docs/architecture-decisions.md`** → synced copies in henry when they exist in core (adapt flow-doc links in henry)
|
|
44
|
+
- **`docs/deployment/`**, **`docs/media-upload-pipeline-design.md`** → sync when shared deploy/media docs change (adapt flow links in henry)
|
|
45
|
+
|
|
46
|
+
**Do not** mirror **`docs/flows/`** into henry-backend. Henry maintains **`docs/core-product-docs.md`** as a pointer/index to core product documentation.
|
|
47
|
+
|
|
48
|
+
Do not overwrite henry-only docs (e.g. **`docs/henry-users-architecture.md`**, calculator architecture, **`docs/core-product-docs.md`**, **`docs/flows/henry/`**).
|
|
49
|
+
|
|
50
|
+
## User-triggered sync
|
|
51
|
+
|
|
52
|
+
Phrases like **"sync rules with henry"** mean: diff **`.cursor/rules/`** and **`.cursor/skills/`** between repos and port any missing or stale shared content.
|
|
@@ -36,17 +36,17 @@ After the user accepts database changes (or if there were none):
|
|
|
36
36
|
|
|
37
37
|
After the user accepts the API layer (or if there was no API layer):
|
|
38
38
|
|
|
39
|
-
- Implement the **Repository** for the domain layer.
|
|
39
|
+
- Implement the **Repository** for the domain layer (Postgres only).
|
|
40
40
|
- Include the **domain model** if data is retrieved from the database.
|
|
41
|
-
-
|
|
41
|
+
- 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/domain-services-and-adapters.md`—not in the repository.
|
|
42
42
|
- Wait for the user to review and accept before proceeding.
|
|
43
43
|
|
|
44
44
|
## 4. Service and Application Layer
|
|
45
45
|
|
|
46
|
-
After the user accepts the repository:
|
|
46
|
+
After the user accepts the repository (and any ports/adapters):
|
|
47
47
|
|
|
48
|
-
- Implement
|
|
49
|
-
- Implement the **application** layer.
|
|
48
|
+
- Implement **domain service(s)** (persistence and any port-backed collaborators); wire in `dependencies.py`.
|
|
49
|
+
- Implement the **application** layer (orchestration across domains and transports—no permanent home for raw I/O libraries).
|
|
50
50
|
- Wire everything together (replace 501 with real logic).
|
|
51
51
|
- Wait for the user to review and accept before proceeding.
|
|
52
52
|
|
|
@@ -14,16 +14,18 @@ Use this architecture split consistently:
|
|
|
14
14
|
- `core_framework/api/...` -> HTTP routers/schemas/dependencies only
|
|
15
15
|
- `core_framework/worker/...` -> schedules/tasks/job handlers only
|
|
16
16
|
- `core_framework/application/...` -> transport-agnostic orchestration shared by API + worker
|
|
17
|
-
- `core_framework/domains/...` -> core domain logic, models, repositories
|
|
17
|
+
- `core_framework/domains/...` -> core domain logic, models, repositories (and optional ports/adapters for non-SQL I/O — see `domain-services-and-adapters`)
|
|
18
18
|
|
|
19
19
|
## Rules
|
|
20
20
|
|
|
21
21
|
- API layer must not contain business orchestration that is shared with workers.
|
|
22
22
|
- Worker layer must not contain domain business rules; call application services only.
|
|
23
|
-
- Worker code must not import **`core_framework.domains`** packages (no direct domain repository/service usage from workers).
|
|
23
|
+
- Worker code must not import **`core_framework.domains`** packages (no direct domain repository/service usage from workers), **except** narrow **housekeeping cron** handlers that call a configured domain service facade (e.g. **`schedule_sweep_stale_avatar_staging`** → **`media_deps.avatar_service`**). Do not add new worker→domain shortcuts without the same documented housekeeping rationale.
|
|
24
24
|
- Application layer must not import FastAPI/Starlette HTTP types.
|
|
25
25
|
- Domain layer must not import API, worker, or application modules.
|
|
26
26
|
- Domain layer must not import global infrastructure except in `dependencies.py`.
|
|
27
|
+
- Repositories are Postgres-only; filesystem, HTTP, SSH, and similar I/O use domain ports/adapters (separate collaborators), not repository methods.
|
|
28
|
+
- Application orchestrates multi-step flows; do not fold shared API+worker orchestration into domain services to avoid “application” existing.
|
|
27
29
|
|
|
28
30
|
## Dependency Direction
|
|
29
31
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use concise English sentences for structlog event strings
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Structured logging (structlog)
|
|
7
|
+
|
|
8
|
+
Use **structlog** (`get_logger`, `logger.info`, `logger.exception`, etc.). The **first positional argument** is the **event** string (JSON `"event"` in non-local environments).
|
|
9
|
+
|
|
10
|
+
## Event strings
|
|
11
|
+
|
|
12
|
+
- Write the event as a **short English sentence** — readable in local console and in log viewers.
|
|
13
|
+
- Use **sentence case**. Prefer past tense for failures (**"Failed to …"**, **"… failed"**).
|
|
14
|
+
- Put **IDs, counts, and context** in **keyword fields**, not in the event string.
|
|
15
|
+
- Do **not** use snake_case machine identifiers as events (for example `process_media_asset_variants_failed`).
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
# ✅ Preferred
|
|
19
|
+
logger.info(
|
|
20
|
+
"Skipped variant processing: staging registry missing",
|
|
21
|
+
owner_user_id=owner_user_id,
|
|
22
|
+
asset_id=asset_id,
|
|
23
|
+
)
|
|
24
|
+
logger.exception("Avatar variant processing failed", asset_id=asset_id)
|
|
25
|
+
|
|
26
|
+
# ❌ Avoid
|
|
27
|
+
logger.info("process_media_asset_variants_skipped_missing_registry", asset_id=asset_id)
|
|
28
|
+
logger.exception("delete_superseded_avatar_finals_failed", asset_id=asset_id)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## When editing
|
|
32
|
+
|
|
33
|
+
- **New** log calls must use English event strings.
|
|
34
|
+
- When you **change** logging in a function or module, convert nearby snake_case events in that same touch to English.
|
|
35
|
+
- Do **not** repo-wide migrate unrelated legacy event names unless the task asks for it.
|
|
@@ -111,6 +111,8 @@ A single `class <Domain>Service:` whose **`__init__`** takes `<Domain>Repository
|
|
|
111
111
|
|
|
112
112
|
When real behavior is added later, apply `repository-read-consistency` (`/.cursor/rules/repository-read-consistency.mdc`) for reads and `_strong` naming.
|
|
113
113
|
|
|
114
|
+
If the bounded context needs filesystem, HTTP, or SSH, add **ports/adapters** or extra domain collaborators per `docs/domain-services-and-adapters.md` — do not put that I/O in `repository.py`.
|
|
115
|
+
|
|
114
116
|
### `__init__.py`
|
|
115
117
|
|
|
116
118
|
Export the **classes** other layers may import: base exception, `<Domain>Repository`, `<Domain>Service`. List them in `__all__` per `domain-imports`. Do **not** re-export module-level `*_repository` / `*_service` instances from `dependencies` here.
|
|
@@ -7,6 +7,14 @@ description: Review code for quality, architecture compliance, and best practice
|
|
|
7
7
|
|
|
8
8
|
When performing a code review, follow this workflow.
|
|
9
9
|
|
|
10
|
+
## Output format (required)
|
|
11
|
+
|
|
12
|
+
Apply **`.cursor/rules/code-review-output.mdc`**. The user wants **actionable findings only**:
|
|
13
|
+
|
|
14
|
+
- Report bugs, wrong behavior, improvements, optimizations, and doc/code contradictions.
|
|
15
|
+
- **Do not** include "what's solid", praise sections, or positive overall assessments.
|
|
16
|
+
- If nothing is wrong, say so in one sentence—no filler.
|
|
17
|
+
|
|
10
18
|
## How to Ask (for the user)
|
|
11
19
|
|
|
12
20
|
**Phrases that trigger this skill:** "review my code", "audit", "comprehensive review", "review comprehensively"
|
|
@@ -28,9 +36,10 @@ When performing a code review, follow this workflow.
|
|
|
28
36
|
Read these files **before** proceeding with the review:
|
|
29
37
|
|
|
30
38
|
1. **`docs/architecture.md`** – Domain boundaries, prohibited practices, and dependency rules
|
|
39
|
+
1. **`docs/domain-services-and-adapters.md`** – Application orchestration vs domain services, ports/adapters, multiple collaborators per bounded context
|
|
31
40
|
1. **`docs/conventions.md`** – Coding conventions, domain rules, caching, Redis, exception handling, API layer rules
|
|
32
41
|
1. **`docs/architecture-decisions.md`** – Intentional design choices; do not flag these as issues
|
|
33
|
-
1. **`.cursor/rules/api-layer.mdc`**, **`.cursor/rules/application-layer.mdc`**, **`.cursor/rules/domain-imports.mdc`**, **`.cursor/rules/domain-caller-context.mdc`** – Apply when reviewing API/application/domain code
|
|
42
|
+
1. **`.cursor/rules/api-layer.mdc`**, **`.cursor/rules/application-layer.mdc`**, **`.cursor/rules/domain-services-and-adapters.mdc`**, **`.cursor/rules/domain-imports.mdc`**, **`.cursor/rules/domain-caller-context.mdc`** – Apply when reviewing API/application/domain code
|
|
34
43
|
|
|
35
44
|
## Step 2: Key Context
|
|
36
45
|
|
|
@@ -41,8 +41,13 @@ jobs:
|
|
|
41
41
|
ALLOWED_HOSTS: ${{ secrets.ALLOWED_HOSTS }}
|
|
42
42
|
AVATAR_HOST_DOMAIN: ${{ secrets.AVATAR_HOST_DOMAIN }}
|
|
43
43
|
AVATAR_DEFAULT_IMAGE: ${{ secrets.AVATAR_DEFAULT_IMAGE }}
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
AVATAR_PUBLISH_ROOT: ${{ secrets.AVATAR_PUBLISH_ROOT }}
|
|
45
|
+
BANNER_HOST_DOMAIN: ${{ secrets.BANNER_HOST_DOMAIN }}
|
|
46
|
+
BANNER_DEFAULT_IMAGE: ${{ secrets.BANNER_DEFAULT_IMAGE }}
|
|
47
|
+
BANNER_PUBLISH_ROOT: ${{ secrets.BANNER_PUBLISH_ROOT }}
|
|
48
|
+
IMAGE_SERVER_HOST: ${{ secrets.IMAGE_SERVER_HOST }}
|
|
49
|
+
IMAGE_SERVER_PORT: ${{ secrets.IMAGE_SERVER_PORT }}
|
|
50
|
+
IMAGE_SERVER_USER: ${{ secrets.IMAGE_SERVER_USER }}
|
|
46
51
|
LOGFIRE_TOKEN: ${{ secrets.LOGFIRE_TOKEN }}
|
|
47
52
|
CORS_ALLOWED_ORIGINS: ${{ secrets.CORS_ALLOWED_ORIGINS }}
|
|
48
53
|
run: |
|
|
@@ -86,6 +91,28 @@ jobs:
|
|
|
86
91
|
sys.exit(1)
|
|
87
92
|
PY
|
|
88
93
|
|
|
94
|
+
- name: Generate image_server_ssh_key from secret
|
|
95
|
+
env:
|
|
96
|
+
IMAGE_SERVER_SSH_PRIVATE_KEY: ${{ secrets.IMAGE_SERVER_SSH_PRIVATE_KEY }}
|
|
97
|
+
run: |
|
|
98
|
+
set -euo pipefail
|
|
99
|
+
|
|
100
|
+
echo "Generating image_server_ssh_key..."
|
|
101
|
+
printf '%s\n' "$IMAGE_SERVER_SSH_PRIVATE_KEY" > image_server_ssh_key
|
|
102
|
+
test -s image_server_ssh_key
|
|
103
|
+
chmod 600 image_server_ssh_key
|
|
104
|
+
|
|
105
|
+
- name: Generate image_server_known_hosts from secret
|
|
106
|
+
env:
|
|
107
|
+
IMAGE_SERVER_KNOWN_HOSTS: ${{ secrets.IMAGE_SERVER_KNOWN_HOSTS }}
|
|
108
|
+
run: |
|
|
109
|
+
set -euo pipefail
|
|
110
|
+
|
|
111
|
+
echo "Generating image_server_known_hosts..."
|
|
112
|
+
printf '%s\n' "$IMAGE_SERVER_KNOWN_HOSTS" > image_server_known_hosts
|
|
113
|
+
test -s image_server_known_hosts
|
|
114
|
+
chmod 644 image_server_known_hosts
|
|
115
|
+
|
|
89
116
|
- name: Upload rendered config.toml, firebase_config.json and docker-compose.yml
|
|
90
117
|
uses: appleboy/scp-action@v1
|
|
91
118
|
with:
|
|
@@ -93,7 +120,7 @@ jobs:
|
|
|
93
120
|
username: ${{ secrets.SERVER_USER }}
|
|
94
121
|
key: ${{ secrets.SERVER_SSH_KEY }}
|
|
95
122
|
port: ${{ secrets.PORT }}
|
|
96
|
-
source: "config.toml,firebase_config.json,docker-compose.yml"
|
|
123
|
+
source: "config.toml,firebase_config.json,docker-compose.yml,image_server_ssh_key,image_server_known_hosts"
|
|
97
124
|
target: "/home/${{ secrets.SERVER_USER }}/app_configs/core-framework"
|
|
98
125
|
|
|
99
126
|
- name: Deploy to Server via SSH
|
|
@@ -113,13 +140,30 @@ jobs:
|
|
|
113
140
|
DOCKERHUB_USERNAME="${{ inputs.dockerhub_username }}"
|
|
114
141
|
CONFIG_PATH="/home/${{ secrets.SERVER_USER }}/app_configs/core-framework/config.toml"
|
|
115
142
|
FIREBASE_CONFIG_PATH="/home/${{ secrets.SERVER_USER }}/app_configs/core-framework/firebase_config.json"
|
|
143
|
+
AVATAR_STAGING_PATH="/home/${{ secrets.SERVER_USER }}/app_configs/core-framework/avatar-staging"
|
|
144
|
+
IMAGE_SERVER_SSH_KEY_PATH="/home/${{ secrets.SERVER_USER }}/app_configs/core-framework/image_server_ssh_key"
|
|
145
|
+
IMAGE_SERVER_KNOWN_HOSTS_PATH="/home/${{ secrets.SERVER_USER }}/app_configs/core-framework/image_server_known_hosts"
|
|
116
146
|
|
|
117
147
|
echo "Pulling latest image"
|
|
118
148
|
DOCKERHUB_USERNAME="$DOCKERHUB_USERNAME" \
|
|
119
149
|
CONFIG_PATH="$CONFIG_PATH" \
|
|
120
150
|
FIREBASE_CONFIG_PATH="$FIREBASE_CONFIG_PATH" \
|
|
151
|
+
AVATAR_STAGING_PATH="$AVATAR_STAGING_PATH" \
|
|
152
|
+
IMAGE_SERVER_SSH_KEY_PATH="$IMAGE_SERVER_SSH_KEY_PATH" \
|
|
153
|
+
IMAGE_SERVER_KNOWN_HOSTS_PATH="$IMAGE_SERVER_KNOWN_HOSTS_PATH" \
|
|
121
154
|
docker compose -f "$COMPOSE_FILE" pull
|
|
122
155
|
|
|
156
|
+
echo "Preparing avatar staging directory and image server SSH key permissions"
|
|
157
|
+
mkdir -p "$AVATAR_STAGING_PATH"
|
|
158
|
+
docker run --rm \
|
|
159
|
+
--user root \
|
|
160
|
+
-v "$AVATAR_STAGING_PATH:/app/avatar-staging" \
|
|
161
|
+
-v "$IMAGE_SERVER_SSH_KEY_PATH:/app/image-server-ssh-key" \
|
|
162
|
+
-v "$IMAGE_SERVER_KNOWN_HOSTS_PATH:/app/image-server-known-hosts" \
|
|
163
|
+
--entrypoint "" \
|
|
164
|
+
"$DOCKERHUB_USERNAME/core-framework:latest" \
|
|
165
|
+
sh -c 'mkdir -p /app/avatar-staging && chown -R appuser:appgroup /app/avatar-staging && chown appuser:appgroup /app/image-server-ssh-key && chmod 600 /app/image-server-ssh-key && chown appuser:appgroup /app/image-server-known-hosts && chmod 644 /app/image-server-known-hosts'
|
|
166
|
+
|
|
123
167
|
echo "Running database migrations (cf-alembic iterates core_framework.bundled_alembic.ALEMBIC_DOMAINS in order)"
|
|
124
168
|
docker run --rm \
|
|
125
169
|
--network host \
|
|
@@ -131,6 +175,9 @@ jobs:
|
|
|
131
175
|
DOCKERHUB_USERNAME="$DOCKERHUB_USERNAME" \
|
|
132
176
|
CONFIG_PATH="$CONFIG_PATH" \
|
|
133
177
|
FIREBASE_CONFIG_PATH="$FIREBASE_CONFIG_PATH" \
|
|
178
|
+
AVATAR_STAGING_PATH="$AVATAR_STAGING_PATH" \
|
|
179
|
+
IMAGE_SERVER_SSH_KEY_PATH="$IMAGE_SERVER_SSH_KEY_PATH" \
|
|
180
|
+
IMAGE_SERVER_KNOWN_HOSTS_PATH="$IMAGE_SERVER_KNOWN_HOSTS_PATH" \
|
|
134
181
|
docker compose -f "$COMPOSE_FILE" up -d \
|
|
135
182
|
--remove-orphans \
|
|
136
183
|
--force-recreate \
|
|
@@ -10,6 +10,9 @@ wheels/
|
|
|
10
10
|
.venv
|
|
11
11
|
.env
|
|
12
12
|
|
|
13
|
+
# Avatar ingest staging (local disk)
|
|
14
|
+
avatar-staging/
|
|
15
|
+
|
|
13
16
|
# Logs
|
|
14
17
|
logs/
|
|
15
18
|
|
|
@@ -21,6 +24,11 @@ htmlcov/
|
|
|
21
24
|
# Firebase
|
|
22
25
|
firebase_config.json
|
|
23
26
|
|
|
27
|
+
# Image server SSH (local dev; production via deploy secrets)
|
|
28
|
+
image-server-known-hosts
|
|
29
|
+
image_server_known_hosts
|
|
30
|
+
image_server_ssh_key
|
|
31
|
+
|
|
24
32
|
# Cursor debug
|
|
25
33
|
firebase-debug.log
|
|
26
34
|
ui-debug.log
|
|
@@ -4,6 +4,45 @@ Notable changes to **core-framework** (import **`core_framework`**). Format foll
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.6.0] - 2026-05-27
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Deploy:** **`[avatar].publish_root`** and **`[banner].publish_root`** are rendered from GitHub environment secrets **`AVATAR_PUBLISH_ROOT`** and **`BANNER_PUBLISH_ROOT`** (via **`config.toml.template`**) so image-server docroots can differ per environment. Local **`config.toml`** defaults are unchanged.
|
|
12
|
+
|
|
13
|
+
## [1.5.0] - 2026-05-26
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Avatar upload (v1):** **`POST /users/me/profile/avatar`**, **`media`** schema, worker variant processing, staging sweep cron, **`[avatar].staging_root`** config. See **`docs/flows/media/upload_pipeline.md`** and **`docs/flows/users/profile.md`**.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Stale staging sweep:** **`delete_avatar_staging_blobs`** returns removed filenames; registry rows are deleted only when the staging file was removed or already missing (failed disk deletes retain the registry row for retry).
|
|
22
|
+
- **ARQ queue mismatch:** API **`RedisQueue`** pool now uses **`default_queue_name=core_framework:queue`**, matching worker consumers, so application enqueues (for example avatar variant processing) are no longer dropped on ARQ's default **`arq:queue`**.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **Breaking:** **`[image_server].known_hosts_path`** is required; **`SshAvatarFinalsPublisherAdapter`** verifies the image server SSH host key (no longer **`known_hosts=None`**). Deploy must provide **`IMAGE_SERVER_KNOWN_HOSTS`** and mount **`/app/image-server-known-hosts`** on the worker. See **`docs/deployment/image-server-ssh.md`**.
|
|
27
|
+
- **Breaking:** **`[avatar].avatar_host_domain`** is **host-only** in **`config.toml`** and deploy secret **`AVATAR_HOST_DOMAIN`** (for example **`avatar.example.com`**, no **`https://`**). The application prepends **`https://`** when assembling public avatar URLs. Values that still include a scheme are normalized at load time.
|
|
28
|
+
- **Breaking:** **`[banner]`** config aligns with **`[avatar]`**: **`banner_host_domain`** + **`default_banner_image`** (deploy secrets **`BANNER_HOST_DOMAIN`**, **`BANNER_DEFAULT_IMAGE`**) replace **`base_url`** / **`default_url`**. Hostname is host-only; public banner URLs derive **`https://`** in application code.
|
|
29
|
+
|
|
30
|
+
### Database
|
|
31
|
+
|
|
32
|
+
- **Media** baseline **`v1_media_init`**: **`media`** schema with **`avatar_staging_registry`** and **`avatar_ingest_lease`**. Hosts must set **`schema_media`** under **`[postgres_schemas]`**.
|
|
33
|
+
- **User** migration **`user_add_avatar_ingest_sequences`**: **`avatar_ingest_sequence`** and **`avatar_applied_sequence`** on **`user_profiles`**.
|
|
34
|
+
- Run **`uv run cf-alembic`** (or the host **`cf-alembic`** step) before deploying code that uses avatar upload or variant processing.
|
|
35
|
+
|
|
36
|
+
### Documentation
|
|
37
|
+
|
|
38
|
+
- **Avatar upload edge limits:** **`docs/deployment/edge-upload-limits.md`**, **`docs/deployment/Caddyfile.example`** — route-scoped reverse-proxy **`max_size`** (**6 MiB** recommended) for **`POST /users/me/profile/avatar`**; updated **`docs/flows/media/upload_pipeline.md`** and **`docs/flows/users/profile.md`**.
|
|
39
|
+
- **Image server SSH:** **`docs/deployment/image-server-ssh.md`**, **`docs/deployment/image-server-known-hosts.example`** — production checklist for **`known_hosts_path`** and **`IMAGE_SERVER_KNOWN_HOSTS`**.
|
|
40
|
+
- **`docs/flows/media/upload_pipeline.md`**: status and HTTP surface aligned with shipped v1 implementation.
|
|
41
|
+
- **`docs/flows/users/profile.md`**: **`POST /users/me/profile/avatar`** flow; **`PATCH`** no longer documents **`avatar_id`** as planned.
|
|
42
|
+
- **`docs/flows/users/avatar.md`**, **`docs/flows/users/change_history.md`**: avatar change path and ingest MIME notes updated.
|
|
43
|
+
- **`docs/flows/users/avatar.md`**, **`docs/flows/users/profile.md`**: **`avatar_host_domain`** documented as host-only; public URLs derive **`https://`** in application code.
|
|
44
|
+
- **`docs/domain-services-and-adapters.md`**: **`banner_host_domain`** / **`default_banner_image`** config shape documented alongside avatar.
|
|
45
|
+
|
|
7
46
|
## [1.4.0] - 2026-05-19
|
|
8
47
|
|
|
9
48
|
### Changed
|
|
@@ -24,7 +63,7 @@ Notable changes to **core-framework** (import **`core_framework`**). Format foll
|
|
|
24
63
|
### Documentation
|
|
25
64
|
|
|
26
65
|
- **`docs/flows/users/avatar.md`**: multi-size **`avatar`** response shape and CDN URL convention.
|
|
27
|
-
- **`docs/flows/media/upload_pipeline.md`**: upload pipeline design
|
|
66
|
+
- **`docs/flows/media/upload_pipeline.md`**: upload pipeline design and v1 implementation reference.
|
|
28
67
|
- **`docs/flows/users/profile.md`**: DOB on get/update flows and validation errors.
|
|
29
68
|
- **`docs/flows/users/change_history.md`**: profile **`date_of_birth`** changes are not written to change history (audit trigger unchanged).
|
|
30
69
|
- **`docs/patch-update-typed-payload.md`**: typed PATCH decisions and patterns for partial-update endpoints.
|
|
@@ -170,4 +209,6 @@ First **SemVer-stable** release per **`docs/package-api.md`**.
|
|
|
170
209
|
[1.2.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.1.1...v1.2.0
|
|
171
210
|
[1.3.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.2.0...v1.3.0
|
|
172
211
|
[1.4.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.3.0...v1.4.0
|
|
173
|
-
[
|
|
212
|
+
[1.5.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.4.0...v1.5.0
|
|
213
|
+
[1.6.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.5.0...v1.6.0
|
|
214
|
+
[unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v1.6.0...HEAD
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-framework
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: Core framework package (import as core_framework)
|
|
5
5
|
Project-URL: Homepage, https://github.com/NepNepFFXIV/core-framework
|
|
6
6
|
Project-URL: Repository, https://github.com/NepNepFFXIV/core-framework
|
|
7
7
|
License-Expression: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Requires-Python: >=3.14
|
|
10
|
+
Requires-Dist: aiofiles>=25.1.0
|
|
10
11
|
Requires-Dist: alembic>=1.18.4
|
|
11
12
|
Requires-Dist: arq>=0.28.0
|
|
12
13
|
Requires-Dist: async-lru>=2.1.0
|
|
13
14
|
Requires-Dist: asyncer>=0.0.14
|
|
14
15
|
Requires-Dist: asyncpg>=0.31.0
|
|
16
|
+
Requires-Dist: asyncssh>=2.23.0
|
|
15
17
|
Requires-Dist: brotli-asgi>=1.6.0
|
|
16
18
|
Requires-Dist: cashews[redis]>=7.5.0
|
|
17
19
|
Requires-Dist: device-detector>=6.2.0
|
|
@@ -22,6 +24,7 @@ Requires-Dist: itsdangerous>=2.2.0
|
|
|
22
24
|
Requires-Dist: logfire[asyncpg,fastapi,httpx,redis]>=4.32.1
|
|
23
25
|
Requires-Dist: mashumaro[orjson]>=3.20
|
|
24
26
|
Requires-Dist: orjson>=3.11.7
|
|
27
|
+
Requires-Dist: pillow>=12.2.0
|
|
25
28
|
Requires-Dist: python-ulid>=3.1.0
|
|
26
29
|
Requires-Dist: structlog>=25.5.0
|
|
27
30
|
Requires-Dist: tenacity>=9.1.4
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from logging.config import fileConfig
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import pool, text
|
|
5
|
+
from sqlalchemy.engine import Connection
|
|
6
|
+
from sqlalchemy.ext.asyncio import async_engine_from_config
|
|
7
|
+
|
|
8
|
+
from alembic import context
|
|
9
|
+
from core_framework.core.settings import load_default_settings
|
|
10
|
+
|
|
11
|
+
settings = load_default_settings()
|
|
12
|
+
|
|
13
|
+
config = context.config
|
|
14
|
+
|
|
15
|
+
if config.config_file_name is not None:
|
|
16
|
+
fileConfig(config.config_file_name)
|
|
17
|
+
|
|
18
|
+
target_metadata = None
|
|
19
|
+
|
|
20
|
+
config.set_main_option("sqlalchemy.url", settings.write_postgres.alembic_postgres_url)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def run_migrations_offline() -> None:
|
|
24
|
+
url = config.get_main_option("sqlalchemy.url")
|
|
25
|
+
context.configure(
|
|
26
|
+
url=url,
|
|
27
|
+
target_metadata=target_metadata,
|
|
28
|
+
literal_binds=True,
|
|
29
|
+
dialect_opts={"paramstyle": "named"},
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
quoted_schema_name = f'"{settings.postgres_schemas.schema_media.replace('"', '""')}"'
|
|
33
|
+
|
|
34
|
+
with context.begin_transaction():
|
|
35
|
+
context.execute(text(f"set search_path to {quoted_schema_name}"))
|
|
36
|
+
context.run_migrations()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def do_run_migrations(connection: Connection) -> None:
|
|
40
|
+
quoted_schema_name = f'"{settings.postgres_schemas.schema_media.replace('"', '""')}"'
|
|
41
|
+
|
|
42
|
+
connection.execute(text(f"create schema if not exists {quoted_schema_name}"))
|
|
43
|
+
connection.execute(text(f"set search_path to {quoted_schema_name}"))
|
|
44
|
+
connection.commit()
|
|
45
|
+
|
|
46
|
+
context.configure(connection=connection, target_metadata=target_metadata)
|
|
47
|
+
|
|
48
|
+
with context.begin_transaction():
|
|
49
|
+
context.run_migrations()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def run_async_migrations() -> None:
|
|
53
|
+
connectable = async_engine_from_config(
|
|
54
|
+
config.get_section(config.config_ini_section, {}),
|
|
55
|
+
prefix="sqlalchemy.",
|
|
56
|
+
poolclass=pool.NullPool,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
async with connectable.connect() as connection:
|
|
60
|
+
await connection.run_sync(do_run_migrations)
|
|
61
|
+
|
|
62
|
+
await connectable.dispose()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def run_migrations_online() -> None:
|
|
66
|
+
asyncio.run(run_async_migrations())
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if context.is_offline_mode():
|
|
70
|
+
run_migrations_offline()
|
|
71
|
+
else:
|
|
72
|
+
run_migrations_online()
|