Перейти к содержанию

Development Log

Хронологический лог завершённых задач проекта App Factory. Новые записи добавляются в начало.


2026-02-09 — AF-25: Bulk Operations + day_index Reorder (Epic B: Content Authoring)

Статус: Завершено Эпик: B — Content Authoring (AF-2)

Backend — BulkOperationService (services/bulk_operations.py): - reorder_day_indices(course, new_order) — two-pass algorithm (temp high values → final values) to avoid unique constraint - bulk_transition(items, new_state) — bulk state transitions with partial failure handling - export_json(items) — export content items with translations to JSON - export_csv(items) — export to CSV with flattened translations (title_{locale}, body_{locale}) - import_json(course, data) — import content items, skip existing day_index

Backend — Admin Actions: - schedule_selected — bulk schedule via BulkOperationService - revert_to_draft_selected — bulk revert to draft - export_selected_json — download JSON export of selected items

Тесты: 18 новых (439 backend total) - test_bulk_operations: 18 тестов (reorder, transitions, export JSON/CSV, import, admin actions)


2026-02-09 — AF-26: Translation Management (Epic B: Content Authoring)

Статус: Завершено Эпик: B — Content Authoring (AF-2)

Backend — TranslationService (services/translation.py): - get_completeness(item, locales) — translation completeness per locale (has_title, has_body, complete) - get_with_fallback(item, locale, default_locale) — fallback chain: requested → default → any → None - get_translation_matrix(course, locales) — coverage matrix for all items in course

Backend — MediaAssetTranslation model: - New model for localized media metadata (alt_text, caption, description per locale) - unique_together (media_asset, locale) - Migration 0006_mediaassettranslation

Backend — Admin: - Standalone MediaAssetAdmin with MediaAssetTranslationInline - MediaAsset now registered as standalone admin (was inline-only)

Тесты: 16 новых (421 backend total) - test_translation_service: 8 тестов (completeness, fallback, matrix) - test_media_asset_translation: 3 теста (CRUD, unique constraint, multiple locales) - test_admin: 5 новых (MediaAssetAdmin registration, inline, changelist)


2026-02-09 — AF-24: Rich Text Editor + Preview (Epic B: Content Authoring)

Статус: Завершено Эпик: B — Content Authoring (AF-2)

Backend — Content Validator (services/content_validator.py): - validate_body_rich(body) — Tiptap JSON schema validation - Whitelist of allowed node types (paragraph, heading, image, audio, etc.) - Supports both Tiptap JSON and {"html": "..."} formats

Backend — Preview Renderer (services/preview.py): - render_body_rich_to_html(body) — JSON→HTML for admin preview - Renders all Tiptap nodes: paragraph, heading, image, audio, video, blockquote, lists - Applies inline marks: bold, italic, underline, strike, code, link

Backend — Admin Preview View: - /admin/app_content/contentitem/<uuid>/preview/?locale=en - 375px mobile phone frame with CSS - Shows title, rendered body, takeaway - MediaAssetInline: added file field, readonly metadata fields

Тесты: 18 новых (405 backend total) - test_content_validator: 7 тестов (valid/invalid nodes, formats) - test_preview: 11 тестов (rendering + admin view)


2026-02-09 — AF-29: XSS Sanitization (Epic B: Content Authoring)

Статус: Завершено Эпик: B — Content Authoring (AF-2)

Backend — Sanitizer Service (apps/content/services/sanitizer.py): - sanitize_html(html) — очистка HTML через nh3 (Rust), разрешённый whitelist тегов - sanitize_plain_text(text) — удаление ВСЕХ HTML-тегов (для title, takeaway) - sanitize_body_rich(body) — рекурсивная санитизация Tiptap JSON и HTML-формата

Backend — Pre-save Signal (apps/content/signals.py): - auto-sanitization ContentTranslation на pre_save: title, takeaway → plain text; body_rich → sanitized JSON - Регистрация через apps.py ready() import

Тесты: 30 новых (387 backend total) - test_sanitizer: 23 теста (HTML tags, XSS vectors, body_rich JSON) - test_rendering_snapshots: 7 тестов (signal sanitization on create/update)


2026-02-09 — AF-23: Asset Upload Pipeline (Epic B: Content Authoring)

Статус: Завершено Эпик: B — Content Authoring (AF-2)

Backend — AssetUploadService (apps/content/services/asset_upload.py): - compute_checksum(file) — SHA-256 хеш файла - extract_image_metadata(file) — ширина/высота изображений - generate_thumbnails(file) — small (150x150) + medium (400x400) через Pillow - detect_mime_type(file) — определение MIME-типа - process_upload(content_item, file, asset_type) — полный пайплайн загрузки

Backend — MediaAsset Model Updates (migration 0005): - Новые поля: file (FileField), thumbnail_small, thumbnail_medium (ImageField), original_filename, mime_type, version, uploaded_at - url и checksum теперь необязательные (blank=True) - file_size default=0

Backend — Storage Configuration: - django-storages[s3] + boto3 + Pillow в requirements.txt - S3/MinIO конфиг в base.py (AWS_*) - InMemoryStorage для тестов - storages добавлен в INSTALLED_APPS

Тесты: 13 новых (357 backend total) - test_asset_upload: 13 тестов (checksum, metadata, thumbnails, mime, process_upload)


2026-02-09 — AF-28: RBAC для админки (Epic B: Content Authoring)

Статус: Завершено Эпик: B — Content Authoring (AF-2)

Backend — DRF Permission Classes (core/permissions.py): - IsContentEditor — content_editor group or superuser - IsReleaseManager — release_manager group or superuser - IsBrandManager — brand_manager group or superuser - IsAnalyst — analyst group or superuser

Backend — Dynamic Admin Navigation (core/admin_navigation.py): - get_navigation(request) — sidebar filtered by user permissions - Superusers see all sections (Content, Quiz, Auth) - Staff users see sections based on group membership - No-group staff users see empty sidebar

Backend — RBAC Groups Setup (setup_rbac_groups management command): - 5 groups: superadmin, content_editor, brand_manager, release_manager, analyst - Idempotent — safe to run multiple times - content_editor: CRUD ContentItem/Translation/MediaAsset, view Course, CRUD QuizQuestion - brand_manager: CRUD AppVariant, manage_branding, view Course - release_manager: publish/bulk ContentItem, view all content/quiz - analyst: view-only across all models

Backend — Custom Permissions (migration 0004): - ContentItem.Meta.permissions: publish_contentitem, bulk_operate_contentitem - AppVariant.Meta.permissions: manage_branding

Backend — API RBAC: - ContentItemTransitionView: now requires IsAdminUser | IsReleaseManager - UNFOLD sidebar: switched from static list to callable core.admin_navigation.get_navigation

Тесты: 24 новых (344 backend total) - test_permissions: 9 тестов (allow/deny per role) - test_admin_navigation: 5 тестов (sidebar visibility) - test_rbac: 10 тестов (groups, admin access, transition API)


2026-02-09 — AF-87: Settings Screen (language, sync status, about)

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Flutter — Settings Provider: - SettingsNotifier — Riverpod @riverpod Notifier for app settings - Reads/writes selected_locale from SharedPreferences - State: selectedLocale, appVersion

Flutter — Settings Screen: - 3 sections: Language (LanguageSelector reuse), Offline & Sync (status + icon), About (version) - Sync status icons: cloud_queue (idle), sync (syncing), cloud_done (success), cloud_off (error) - Error message shown as subtitle when sync fails

Flutter — Integration: - TodayScreen AppBar: added settings gear icon → navigates to /settings - AppRouter: added /settings route - ARB files: 9 new i18n keys (EN + RU) for settings strings - Reuses LanguageSelector widget from onboarding

Тесты: 12 новых (170 Flutter total) - settings_provider_test: 5 тестов (initial state, language changes, version) - settings_screen_test: 7 тестов (sections, language, sync states, version)


2026-02-09 — AF-86: Onboarding Flow (language, welcome, first sync)

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Flutter — Onboarding Provider: - OnboardingNotifier — Riverpod @riverpod Notifier for onboarding lifecycle - State: isComplete, currentPage, selectedLocale - Persists to SharedPreferences: onboarding_complete (bool), selected_locale (string) - Methods: selectLocale(), nextPage(), previousPage(), completeOnboarding()

Flutter — Onboarding Screen: - 3-page PageView: Welcome → Language Selection → Ready - LanguageSelector widget: EN (English) + RU (Русский) with flags - Page indicator dots, Back/Next navigation - "Get Started" completes onboarding and redirects to splash (triggers bootstrap) - Next disabled on language page until a language is selected

Flutter — Integration: - SplashScreen updated: checks onboardingState.isComplete → routes to /onboarding or /today - AppRouter: added /onboarding route - ARB files: 10 new i18n keys (EN + RU) for onboarding strings - app_test.dart updated: provides SharedPreferences + mocked bootstrapProvider

Тесты: 16 новых (158 Flutter total) - onboarding_provider_test: 8 тестов (state, locale, pages, persistence) - onboarding_screen_test: 8 тестов (welcome, language, navigation, completion)


2026-02-09 — AF-19: Sync Engine (manifest pull, diff, download)

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Flutter — Data Layer: - ContentManifest Freezed DTOs: ContentManifest, ManifestItem, ManifestAsset (3 classes, JSON serialization) - ContentDao extensions: getContentHashes(courseId) returns ID→hash map, deleteByIds(ids) cascades to translations/media - BootstrapService.getContentManifest() updated: accepts courseId + optional sinceVersion query params - ContentRepository.cacheContentItem() made public (was _cacheContentItem) for SyncService access

Flutter — Sync Engine: - SyncService — core sync logic: - runFullSync(courseId, locale) → orchestrates manifest pull + diff + apply - pullManifest(courseId, locale) → reads local content_version from SyncMetadata → calls manifest API → compares hashes → returns SyncDiff - applyDiff(diff, courseId, locale) → deletes removed items → downloads changed items → caches via ContentRepository - Updates sync metadata (content_version, last_sync_time) after each sync - Resilient: continues on individual item download failure (logs warning) - SyncDiff — computed diff (itemsToUpdate, itemsToDelete, remoteVersion) - SyncResult — sync outcome (itemsUpdated, itemsDeleted, newVersion, isUpToDate)

Flutter — SyncProvider: - SyncNotifier — Riverpod notifier with states: idle → syncing → success/error - Debounces concurrent sync calls (returns early if already syncing) - Exposes SyncState with status, lastResult, errorMessage

26 новых Flutter тестов (142 Flutter total): - ContentManifest DTO (5): fromJson full, ManifestItem, ManifestAsset, roundtrip, empty - ContentDao (4): getContentHashes map, empty course, deleteByIds cascade, empty list - SyncService (12): pullManifest (6 — empty, sinceVersion, new items, changed hash, matching hash, deletions), applyDiff (3 — download, delete, failure resilience), runFullSync (3 — up-to-date, download + metadata, deletions) - SyncProvider (5): initial idle, success transition, error transition, debounce, isUpToDate

Итого: 320 backend + 142 Flutter = 462 тестов, 36 коммитов на develop


2026-02-09 — AF-89: Content Versioning Strategy

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

  • Auto content_hash: compute_content_hash() — SHA-256 from translations + structure
  • Hash auto-set on publish (no manual entry needed)
  • content_version auto-increment on publish via F("content_version") + 1
  • DeletedContentItem model — tracks archived published items (course, content_item_id, content_version)
  • Migration: 0003_add_deleted_content_item
  • Manifest content_item_id field added to each item
  • since_version filtering: >= content_version → empty response (client up-to-date)
  • deleted_item_ids populated from DeletedContentItem for delta sync
  • 12 новых тестов (320 backend total):
  • TestAutoContentHash (4): sha256 prefix, changes with translation, stable, updated on publish
  • TestContentVersionBump (2): single publish, multiple sequential
  • TestDeletedContentTracking (2): archive published creates record, draft→archive no record
  • TestManifestWithVersioning (4): content_item_id, since_version empty, since_version changes, deleted_item_ids

2026-02-09 — AF-42: Timezone-aware Streak and Day Boundary

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

  • _resolve_today(client_timezone) — вычисляет "сегодня" в таймзоне клиента (IANA name, e.g. "Asia/Tokyo")
  • _compute_time_offset(client_timezone) — UTC offset в секундах для клиентского таймзона
  • complete_day и build_progress_state принимают client_timezone (optional)
  • GET /progress принимает client_timezone query param
  • POST /progress/complete_day принимает client_timezone в body
  • Graceful fallback: невалидный таймзон → UTC
  • Используется Python zoneinfo (stdlib, без внешних зависимостей)
  • 12 новых тестов (308 backend total):
  • TestResolveToday (4): UTC default, positive offset, negative offset, invalid fallback
  • TestComputeTimeOffset (5): positive, negative, UTC, none, invalid
  • TestTimezoneAwareStreak (2): consecutive with timezone, same UTC day different local days
  • TestBuildProgressStateWithTimezone (2): computed offset, zero without timezone

2026-02-09 — AF-91: Dockerfile — Multistage (Dev + Production)

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

  • Multistage Dockerfile: base → dev (gunicorn --reload) → production (non-root user)
  • .dockerignore: excludes .venv, pycache, .git, .env
  • docker-compose.yml: backend service with build context, depends_on db+redis
  • 6 новых тестов (295 backend total)

2026-02-09 — AF-93: Celery Configuration — Beat Schedule, Retry Policies

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

  • Добавлен CELERY_BEAT_SCHEDULE: publish_scheduled_content каждые 15 минут
  • Task retry: autoretry_for(Exception), max_retries=3, retry_backoff=60s
  • Time limits: hard=300s, soft=240s
  • CELERY_TASK_ACKS_LATE=True, CELERY_TASK_REJECT_ON_WORKER_LOST=True
  • 4 новых теста (289 backend total)

2026-02-09 — AF-90: Server Infrastructure (Verified & Closed)

Статус: Завершено (реализовано в AF-100/AF-101) Эпик: A — Platform Foundation (AF-1)

  • PostgreSQL 16 (docker, port 5433) — healthy
  • Redis 7 (docker, port 6380) — healthy
  • Nginx с SSL (HTTP/2) — active
  • Gunicorn (systemd, unix socket) — active
  • Celery (systemd, Redis broker) — active

2026-02-09 — AF-92: OpenAPI Auto-generation (Verified & Closed)

Статус: Завершено (реализовано ранее) Эпик: A — Platform Foundation (AF-1)

  • drf-spectacular 0.28.0
  • Swagger UI: /api/docs/ — 200 OK
  • ReDoc: /api/redoc/ — 200 OK
  • OpenAPI schema: /api/schema/ — 200 OK

2026-02-09 — AF-27: Content Lifecycle — State Machine, API, Celery Task

Статус: Завершено Эпик: B — Content Authoring (AF-3) Jira: AF-27 (Content lifecycle: draft/scheduled/published)

Реализовано: - State machine (apps/content/services/lifecycle.py): - Валидные переходы: draft→published, draft→scheduled, scheduled→draft, scheduled→published, published→archived, archived→draft - transition_content_item() — валидация + переход + timestamps - validate_publishable() — проверка: ≥1 translation + content_hash - publish_scheduled_items() — автопубликация просроченных scheduled items - InvalidTransitionError — extends ValidationError (автоматический 400) - Новые поля в ContentItem: - scheduled_publish_at (DateTimeField, nullable) - published_at (DateTimeField, nullable) - API endpoint: POST /api/v1/content/items/{id}/transition — admin-only, body: {"state": "published"} - Celery task: publish_scheduled_content — для periodic beat - Admin actions: "Publish selected" и "Archive selected" в ContentItemAdmin (Django Unfold) - Миграция: 0002_add_lifecycle_fields

Тесты: 21 новый (285 backend всего): - TestValidTransitions (6): draft→published/scheduled, scheduled→draft/published, published→archived, archived→draft - TestInvalidTransitions (3): draft→archived, published→draft, published→scheduled - TestPublishValidation (5): requires translation, requires content_hash, requires scheduled_publish_at, sets published_at, validate_publishable - TestCeleryTask (2): publishes past-due, skips future - TestTransitionAPI (5): requires admin, anonymous forbidden, valid transition, invalid returns 400, invalid state value

Анализ: ruff check — 0 issues


2026-02-09 — AF-18: Drift Local Database (Offline-first)

Статус: Завершено Эпик: A — Platform Foundation (AF-1) Jira: AF-18 (Local database with Drift)

Реализовано: - 5 Drift таблиц (frontend/lib/data/local/database/tables/): - ContentItems — кешированные content items (text PK = UUID) - ContentTranslations — переводы с composite PK (contentItemId, locale) - MediaAssets — медиа-ассеты с FK на ContentItems - ProgressEntries — прогресс пользователя с isDirty флагом для sync - SyncMetadataEntries — key-value store для sync metadata - AppDatabase (app_database.dart) — Drift database class, schemaVersion 1 - 3 DAO (plain classes, без @DriftAccessor — избегаем circular imports): - ContentDao — CRUD для items, translations, media assets; clearCourse - ProgressDao — upsert/get/markDirty/getDirtyEntries - SyncMetadataDao — key-value operations, getLastSyncTime - Riverpod provider (database_provider.dart) — @Riverpod(keepAlive: true) singleton - Cache-first repositories: - ContentRepository обновлён: DB cache → API fallback → cache result - ProgressRepository обновлён: local-first write + dirty marking для offline

Тесты: 28 новых (116 Flutter всего): - content_dao_test: 14 (insert, getById, getByDayIndex, getByCourse, upsert translation/media, clear, conflict update) - progress_dao_test: 6 (upsert, get, update, mark dirty, get dirty, upsert resets dirty) - sync_metadata_dao_test: 5 (set/get, null key, overwrite, last sync time) - app_database_test: 3 (creation, schema version, table accessibility)

Зависимости: drift 2.28.2, sqlite3_flutter_libs 0.5.28, path_provider 2.1.5, drift_dev 2.22.2

Анализ: dart analyze — 0 issues


2026-02-09 — AF-22: Admin UX с Django Unfold

Статус: Завершено Эпик: B — Content Authoring (AF-3) Jira: AF-22 (Admin UX: AppVariant management)

Реализовано: - Django Unfold 0.78.1 — Material UI тема для Django Admin - Установка: ПЕРЕД django.contrib.admin в INSTALLED_APPS - Конфигурация UNFOLD в settings: site title, sidebar navigation с Material icons - Content Admin (apps/content/admin.py): - CourseAdmin — list_display, search, readonly fields - AppVariantAdmin — fieldsets (Basic, Localization, Branding, Daily Rules, Offline Policy, Monetization, Features), фильтры, поиск - ContentItemAdmin — inlines (MediaAssetInline, ContentTranslationInline), lifecycle actions, state filter, content_hash_short display - Quiz Admin (apps/quiz/admin.py) — обновлён на Unfold ModelAdmin: - QuizQuestionAdmin — prompt_short, difficulty filter, cooldown - QuizSessionAdmin — id_short, status filter, score_total - QuizAnswerAdmin — is_correct filter - Auth Admin (apps/auth/admin.py): - DeviceSessionAdmin — device_install_id, last_seen_at

Тесты: 31 новый (264 до lifecycle → 285 с lifecycle): - test_admin.py (content): 18 тестов — registration, list_display, fieldsets, inlines, search, changelist/change_form accessibility - test_admin.py (quiz): 13 тестов — registration, list_display, search, short methods, changelist/change_form accessibility

Анализ: ruff check — 0 issues


2026-02-09 — AF-101: Nginx, Gunicorn, SSL, Celery — production deployment

Статус: Завершено Jira: AF-101 (INFRA: Nginx, Gunicorn, SSL, Celery — production deployment)

Реализовано: - Nginx: reverse proxy конфиги для app-factory.online и dev.app-factory.online - SSL через Let's Encrypt (certbot), HTTP→HTTPS редирект, www→non-www - Rate limiting: auth 5r/s, API 30r/s - Security headers: X-Content-Type-Options, X-Frame-Options, Referrer-Policy - Static/media файлы отдаются Nginx напрямую (минуя Gunicorn) - Gunicorn: 3 workers, unix socket /run/app-factory/gunicorn.sock, worker recycling - Systemd: app-factory-backend.service (Type=notify) + app-factory-celery.service - Celery app: config/celery.py с autodiscover_tasks, config/__init__.py обновлён - Django settings: - STATIC_ROOT = BASE_DIR / "staticfiles" в base.py - SECURE_PROXY_SSL_HEADER в production.py и staging.py - SSL/security настройки для staging (HSTS, secure cookies, SSL redirect) - Setup script: infra/scripts/setup-server.sh для одноразового провизионирования - DNS документация: infra/docs/dns-setup.md

Архитектура:

DNS (Bunny.net) → VPS 87.106.6.204
  → Nginx (80/443, SSL termination)
    → Gunicorn (unix socket)
      → Django (staging settings)
    → Static/Media (direct)
  → Celery worker (Redis broker)

Тесты: 233 backend (включая 3 новых celery) — все зелёные Анализ: ruff check — 0 issues


2026-02-09 — AF-100: Domain configuration for app-factory.online

Статус: Завершено Jira: AF-100 (INFRA: Domain configuration for app-factory.online)

Реализовано: - Django settings: добавлен CSRF_TRUSTED_ORIGINS в base.py, production.py, staging.py - staging.py: дефолтные значения для dev.app-factory.online (ALLOWED_HOSTS, CSRF, CORS) - .env / .env.example: добавлены переменные CSRF_TRUSTED_ORIGINS, CORS_ALLOWED_ORIGINS с примерами для production/staging - Flutter: создан EnvConfig (frontend/lib/core/config/env_config.dart) — мульти-окружения dev/staging/production с разными API URL - Flutter: удалён ApiConstants.baseUrl, заменён на EnvConfig.apiBaseUrl в api_client.dart - CI/CD: deploy-production.yml и deploy-staging.yml — export env vars для доменов - CLAUDE.md: обновлён заголовок, добавлена секция "Домены"

Домены: - Production: app-factory.online (Bunny.net CDN → VPS) - Dev/Staging: dev.app-factory.online - API: https://app-factory.online/api/v1 (prod), https://dev.app-factory.online/api/v1 (dev)

Тесты: 230 backend + 88 Flutter = 318 тестов — все зелёные - 4 новых: EnvConfig (dev/staging/production URLs, default environment)

Анализ: ruff check — 0 issues, dart analyze — 0 issues


2026-02-09 — AF-88: i18n — Локализация hardcoded строк

Статус: Завершено Эпик: B — Flutter Client Foundation (AF-2) Jira: AF-88 (Flutter: i18n setup with intl/ARB for UI localization)

Реализовано: - Подключены AppLocalizations.localizationsDelegates и supportedLocales в app.dart - Заменены hardcoded строки в TodayScreen: Day N, X / Y days, Mark Complete, Completed! - Заменены hardcoded строки в CompletionScreen: Day N Complete!, streak days, personal best, Continue, Start Quiz - 5 новых ARB ключей: dayComplete, personalBest, continueButton, progressDays, completedBanner - Русские переводы: "День N завершён!", "Новый личный рекорд!", "Продолжить" и др.

Тесты: 84 Flutter тестов — все зелёные (тесты обновлены для l10n delegates)

Анализ: dart analyze — 0 issues


2026-02-09 — AF-57: Quiz Trigger on CompletionScreen

Статус: Завершено Эпик: F — Engagement & Retention (AF-5) Jira: AF-57 (F-7: Quiz trigger logic: when quizzes appear)

Реализовано: - Добавлен quizEnabled флаг в TodayData (из appVariantConfig.features['quiz']) - Кнопка "Start Quiz" (OutlinedButton.icon) на CompletionScreen, видна только при quizEnabled: true - Навигация: /quiz?course_id=X

Тесты: 9 новых (84 Flutter всего): - completion_screen_test: renders celebration, day text, streak, personal best (show/hide), continue button, quiz button (show/hide)

Анализ: dart analyze — 0 issues


2026-02-09 — AF-55: Flutter Quiz Feature (Data Layer + UI)

Статус: Завершено Эпик: F — Engagement & Retention (AF-5) Jira: AF-55 (F-5: Flutter: Swipe cards UI with haptics and scoring)

Реализовано (Data Layer): - 8 Freezed DTO классов в quiz_session.dart (QuizSession, QuizQuestion, QuizScoring, QuizCooldowns, QuizAnswerResponse, SessionProgress, QuizCompleteResponse, QuizResult) - QuizService — тонкий HTTP wrapper (3 метода: getNextSession, submitAnswer, completeSession) - QuizRepository — бизнес-логика (3 метода с Freezed DTO маппингом) - Quiz endpoints в api_constants.dart

Реализовано (UI): - QuizState — Riverpod Notifier с AsyncValue<QuizData?> (loadSession, answerQuestion, completeQuiz, clearLastAnswer) - QuizScreen — ConsumerStatefulWidget с swipe cards + feedback overlay (1.5s) - QuizResultScreen — результаты (score, accuracy, correct count) - SwipeCard — GestureDetector с AnimatedController, порог 100px, haptic feedback - ScoreDisplay — счёт + прогресс-бар + "Question X of Y" - GoRouter маршруты: /quiz, /quiz/result - i18n: 13 ключей (quizTitle, quizCorrect, quizIncorrect, quizScore, quizQuestionOf, quizComplete и др.) в EN + RU

Тесты: 39 новых Flutter тестов (75 всего): - quiz_session_test: 9 (DTO fromJson) - quiz_repository_test: 6 (mock service) - quiz_provider_test: 9 (QuizData unit tests) - swipe_card_test: 6 (render, swipe right/left, bounce, disabled) - score_display_test: 4 (render score, question count, progress, zero) - quiz_result_screen_test: 5 (render, trophy/school icon, zero)

Анализ: dart analyze — 0 issues, dart format — чисто


2026-02-09 — AF-95: Seed Database Command

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Реализовано: - Management command seed_sample_data для загрузки тестовых данных - Источник: docs/sample-content-fixture.json (Pushkin 60 Days of Poetry) - Создаёт: 1 Course, 1 AppVariant, 5 ContentItems, 7 MediaAssets, 8 Translations, 10 QuizQuestions - Идемпотентный (get_or_create), поддержка --flush - @transaction.atomic для целостности - structlog логирование

Тесты: 8 новых (230 всего в backend) - test_creates_course, test_creates_app_variant, test_creates_content_items - test_creates_media_assets, test_creates_translations, test_creates_quiz_questions - test_idempotent, test_flush_and_recreate

Анализ: ruff check — 0 issues


2026-02-08 — Quiz Backend Module

Статус: Завершено Эпик: F — Engagement & Retention Коммит: 4ad98b8 (develop)

Реализовано: - Новое Django-приложение apps/quiz — полный цикл true/false swipe quiz - Модели: QuizQuestion (банк вопросов с difficulty, cooldown, linked items, tags), QuizSession (сессия с scoring), QuizAnswer (ответ с idempotency) - QuizService — service layer со static methods: - get_next_session — отбор вопросов с cooldown фильтром, создание сессии - submit_answer — идемпотентный через unique_together + transaction.atomic() - complete_session — идемпотентное завершение, accuracy + recommended_review_tags - 3 API эндпоинта: - GET /api/v1/quiz/session/next?course_id=UUID → 200/204 - POST /api/v1/quiz/session/{id}/answer → 200 - POST /api/v1/quiz/session/{id}/complete → 200 - @extend_schema для drf-spectacular - Структурированное логирование (structlog)

Тесты: 50 новых (222 всего в backend) - test_models.py: 10 тестов - test_serializers.py: 12 тестов - test_services.py: 17 тестов (cooldown, idempotency, score accumulation, review tags) - test_views.py: 11 тестов (API integration, auth, validation)

Анализ: ruff check — 0 issues


2026-02-08 — Flutter Daily Flow (AF-17)

Статус: Завершено Эпик: A — Platform Foundation (AF-1) Коммит: 759a824 (develop)

Реализовано: - TodayScreen — главный экран: AppBar со streak, прогресс-бар, контент-блоки, takeaway, кнопка "Mark Complete" - CompletionScreen — экран завершения дня: празднование, streak, "New personal best!" conditional - ContentBlockRenderer — рендерит body_rich JSON → Flutter виджеты (heading, paragraph, quote, fact) - StreakIndicator — иконка огня (filled/outlined) + счётчик streak - TodayState Notifier + TodayData — агрегация bootstrap данных для today screen - todayContentProvider — async провайдер контента (из bootstrap или content API) - ContentRepository + ProgressRepository — репозитории для API вызовов - Обновлён GoRouter: /today → TodayScreen, /completion → CompletionScreen - CLAUDE.md в каждой поддиректории daily/

Тесты: 12 новых (36 Flutter всего) - today_provider_test.dart: 3 теста (fromBootstrap, progressPercent, todayItem) - content_block_renderer_test.dart: 6 тестов (paragraph, heading, quote, fact, multiple, empty) - streak_indicator_test.dart: 3 теста (count, fire icon, outlined)

Анализ: dart analyze — 0 issues, dart format — clean


2026-02-08 — Flutter Bootstrap Client (AF-16)

Статус: Завершено Эпик: A — Platform Foundation (AF-1) Коммит: 510ca9e (develop)

Реализовано: - 23 Freezed DTO класса: AuthResponse, ContentItem, LocalizedContent, MediaGroup, MediaAsset, ProgressState, StreakInfo, AppVariantConfig, AppBranding, AppName, BrandTheme, BrandAssets, LocalesConfig, CourseRef, DailyRules, CatchUpPolicy, StreakFreeze, OfflinePolicy, MediaPolicy, MonetizationConfig, EntitlementDef, PaywallConfig, BootstrapResponse, CourseSummary - TokenStorage (SharedPreferences) — access/refresh tokens, user_id, device_install_id - AuthInterceptor — Bearer header, 401 → auto-refresh → retry - AuthRepository — ensureAuthenticated (генерирует UUID device_install_id), reauthenticate - BootstrapRepository — fetchBootstrap → parsed BootstrapResponse - bootstrapProvider — async: auth → bootstrap flow - SplashScreen — ConsumerWidget с loading/error/retry, навигация на /today - Обновлён main.dart: SharedPreferences init + provider override

Тесты: 24 (13 новых) - auth_response_test.dart (3), content_item_test.dart (3), bootstrap_response_test.dart (3) - auth_repository_test.dart (4) с mocktail

Анализ: dart analyze — 0 issues, dart format — clean


2026-02-08 — Flutter Scaffold (AF-15)

Статус: Завершено Эпик: A — Platform Foundation (AF-1) Коммит: a39f4ac (develop)

Реализовано: - Flutter проект (flutter create) с полной структурой каталогов - State Management: Riverpod 2.6 с code-gen (@riverpod) - Routing: GoRouter 14.x — декларативная маршрутизация, SplashScreen - HTTP Client: Dio 5.x с _ErrorInterceptor (AppException/NetworkException) и _LoggingInterceptor - Theme: Material 3 (light/dark), будет расширена из AppVariant config - API Services: AuthService (anonymous, refresh) + BootstrapService (bootstrap, content, progress) - i18n: gen-l10n (en/ru), 11 строк с plural forms - Error Handling: AppException.fromJson() — маппинг backend ErrorEnvelope - CLAUDE.md: в каждой директории (app/, core/, data/, features/, l10n/)

Тесты: 11 (app smoke + AppException unit) Анализ: dart analyze — 0 issues, dart format — clean

Пакеты: flutter_riverpod, riverpod_annotation, go_router, dio, freezed_annotation, json_annotation, shared_preferences, uuid, intl, logger, build_runner, freezed, json_serializable, riverpod_generator, mocktail


2026-02-08 — GET /content/items/{content_item_id}

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • GET /api/v1/content/items/{content_item_id} — получение контента по UUID (без проверки доступа дня)
  • Новый метод ContentService.get_content_item_by_id() — переиспользует _build_media_group() и locale resolution
  • Отличие от /content/days/{n}: не требует UserProgress, предназначен для offline-сценария (контент уже скачан)

Затронутые компоненты

  • Backend: apps/content/ (services.py, views.py, urls.py)

Тесты

  • tests/apps/content/test_views_item.py (6 тестов)
  • Всего в проекте: 172 теста

2026-02-08 — Sample Content Fixture

Статус: Завершено

Что сделано

  • Создан docs/sample-content-fixture.json — полный пример данных для одного приложения «Пушкин: 60 дней поэзии»
  • Раздел database: все сущности БД (Course, AppVariant, 5 ContentItems, 7 MediaAssets, 8 ContentTranslations, UserProgress, 3 DayCompletions)
  • Раздел api_responses: точные JSON-ответы 5 реализованных эндпоинтов (bootstrap, content/days, manifest, progress, complete_day)
  • Реалистичный контент: дни 1-5 про Пушкина, body_rich с heading/paragraph/quote/fact, мультиязычность ru+en
  • Полная конфигурация AppVariant: branding, daily_rules, offline_policy, monetization, features

Цель

Валидация текущей структуры моделей и API-контракта перед выбором следующего направления разработки.


2026-02-08 — AF-13: Progress API

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • GET /api/v1/progress?course_id=UUID — текущий прогресс пользователя (ProgressState)
  • POST /api/v1/progress/complete_day — идемпотентное завершение дня
  • Streak-алгоритм: yesterday=+1, gap=reset to 1, same day=no change
  • Day unlock: max(current_day_index, day_index + 1)
  • Миграция DayCompletion.completed_at: auto_now_add=Truedefault=timezone.now (offline-first, клиентский timestamp)
  • Идемпотентность через unique_together + transaction.atomic() savepoint
  • Сериализаторы: ProgressQuery, CompleteDayRequest, ProgressState, CompleteDayResponse, Streak, Engagement
  • Service layer: ProgressService (get_progress_state, build_progress_state, complete_day, _update_streak)

Затронутые компоненты

  • Backend: apps/progress/ (serializers, services, views, urls, migration), config/api_urls.py

Тесты

  • tests/apps/progress/test_serializers.py (6 тестов)
  • tests/apps/progress/test_services.py (15 тестов)
  • tests/apps/progress/test_views.py (13 тестов)
  • tests/apps/progress/test_models.py (+1 тест: completed_at_accepts_client_timestamp)
  • Всего в проекте: 166 тестов, 97% coverage

2026-02-07 — Система документации: drf-spectacular + MkDocs + Skill /write-docs

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • drf-spectacular: автогенерация OpenAPI, Swagger UI (/api/docs/), ReDoc (/api/redoc/)
  • MkDocs Material: статический сайт документации с поиском и навигацией
  • Skill /write-docs: обязательное обновление документации после каждой задачи
  • Начальное наполнение: index, getting-started, development-log, ADR index
  • Интеграция в workflow: DoD, sync-jira, CLAUDE.md

Затронутые компоненты

  • Backend: config/settings/base.py, config/urls.py, apps/auth/views.py, apps/bootstrap/views.py, apps/content/views.py
  • Docs: index.md, getting-started.md, development-log.md, api-contract.md, adr/index.md
  • Infra: Makefile, mkdocs.yml, requirements-docs.txt, .github/workflows/
  • Skills: .claude/skills/write-docs/

Тесты

  • Добавлено: 8 тестов (test_openapi_schema.py)
  • Всего в проекте: 131 тестов

2026-02-07 — AF-12: Bootstrap + Content API

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • GET /api/v1/bootstrap — полная инициализация приложения (config + course + progress + today_item)
  • GET /api/v1/content/days/{day_index} — контент конкретного дня с локализацией и медиа
  • GET /api/v1/content/manifest — манифест для offline-синхронизации
  • BootstrapService: resolve_app_variant, get_or_create_progress, compute_today_status, should_return_304, build_bootstrap_response
  • ContentService: resolve_locale, get_content_item_for_day, check_day_access, get_content_manifest
  • Сериализаторы: 5 для content, 1 для bootstrap

Затронутые компоненты

  • Backend: apps/bootstrap/ (services, serializers, views, urls), apps/content/ (services, serializers, views, urls), config/api_urls.py

Тесты

  • tests/apps/bootstrap/test_services.py (16 тестов)
  • tests/apps/bootstrap/test_views.py (9 тестов)
  • tests/apps/content/test_services.py (16 тестов)
  • tests/apps/content/test_views.py (9 тестов)
  • Всего в проекте: 123 теста, 97% coverage

2026-02-07 — AF-11: Content & Progress Data Models

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • Модели контентного домена: Course, AppVariant, ContentItem, MediaAsset, ContentTranslation
  • Модели прогресса: UserProgress, DayCompletion
  • UUID PK, TextChoices для enums, JSONField для конфигов
  • Миграции для apps/content (0001) и apps/progress (0001)

Затронутые компоненты

  • Backend: apps/content/models.py, apps/progress/models.py

Тесты

  • tests/apps/content/test_models.py (26 тестов)
  • tests/apps/progress/test_models.py (8 тестов)

2026-02-07 — AF-94: ADR-001 State Management — Riverpod

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • Architecture Decision Record: выбор Riverpod 2.x для Flutter state management
  • Документ: docs/adr/001-state-management-riverpod.md
  • Обоснование: compile-time safety, native async, testability, code generation

2026-02-07 — AF-10: Anonymous JWT Auth

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • Модель DeviceSession (apps/auth/models.py)
  • POST /api/v1/auth/anonymous — анонимная авторизация по device_install_id
  • POST /api/v1/auth/refresh — обновление токенов с ротацией
  • AuthService с идемпотентной логикой (create or resume)
  • Сериализаторы с валидацией

Тесты

  • tests/apps/auth/test_models.py, test_serializers.py, test_services.py, test_views.py

2026-02-07 — AF-14: ErrorEnvelope

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • Единый формат ошибок: {error: {code, message, details, request_id}}
  • Кастомный exception handler (core/exceptions.py)
  • Маппинг HTTP кодов → error codes (validation_error, authentication_error, etc.)

Тесты

  • tests/core/test_error_handling.py

2026-02-07 — AF-20: CorrelationID Middleware

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • CorrelationIDMiddleware (core/middleware/correlation.py)
  • Генерация/проброс X-Correlation-ID через запрос
  • Интеграция с structlog через contextvars

Тесты

  • tests/core/test_correlation_middleware.py

2026-02-07 — AF-21: TDD Infrastructure

Статус: Завершено Эпик: A — Platform Foundation (AF-1)

Что сделано

  • Инициализация Django 5.2 + DRF 3.16
  • Настройка pytest + pytest-django
  • Структура settings: base/dev/test/staging/production
  • Docker Compose: PostgreSQL 16 + Redis 7
  • Pre-commit hooks: ruff, isort, dart format, conventional commits, secrets detection
  • 5 GitHub Actions workflows

Тесты

  • Базовый smoke-тест (test_health.py)