## Backend (7 production files + 6 test files) ### P0-2: LLM output truncation cascade fix - _token_budget.py: OUTPUT_PER_ROW_PER_LANG 120→200, OUTPUT_SAFETY_FACTOR 0.70→0.55 - Prevents finish_reason=length → split → retry cascade (3 calls → 1 call per batch) - P2-8: added qwen-flash/qwen-plus/qwen-max/qwen-coder to PROVIDER_DEFAULTS ### P1-4/P1-5: EncryptionManager singleton - encryption.py: get_encryption_manager() process-wide singleton - llm_provider.py: use singleton instead of new EncryptionManager() per batch - Eliminates ~90 redundant Fernet key validations per translation run ### P1-6: Cache-hit log aggregation - _batch_proc.py: one log per batch (batch_rows + cache_hits) instead of per-row - 1076 log lines → ~30 per run ### P1-7: Timezone-aware datetime fix - scheduler.py: _ensure_aware() helper for naive DB datetime → UTC-aware - Fixes TypeError in scheduled translation concurrency check ### P2-9: Connection test timeout - connection_service.py: asyncio.wait_for(15s) on all dialect tests - Prevents 2-minute UI hangs from DNS/TCP stalls ### Trace ID propagation - middleware/trace.py: inject x-trace-id response header via ASGI send wrapper ### Test fixes & integration tests - test_scheduler.py: AsyncMock for execute_run, mock get_async_job_runner - test_sql_insert_service.py: AsyncMock for execute_sql - test_token_budget.py: batch_size 50→45 for new OUTPUT_PER_ROW_PER_LANG=200 - test_encryption.py: +2 singleton tests - test_scheduler_ensure_aware.py: +4 (naive→aware, passthrough, None, subtraction) - test_batch_classify_persist.py: +2 cache-hit aggregation tests - test_connection_service_edge.py: +2 timeout tests - test_trace_middleware.py: +4 x-trace-id header tests - test_token_budget.py: +4 qwen-flash/O200 tests ## Frontend (7 production files + 5 test files) ### Trace ID propagation - api.ts: _captureTraceId() reads x-trace-id → setTraceId() in fetchApi/requestApi/postApi/deleteApi ### Duplicate datasource columns fetch - ConfigTabForm.svelte: guard availableColumns.length === 0 before fetch ### Admin pages Svelte 5 runes fix - admin/users/+page.svelte: plain let → () for all template-bound vars - admin/roles/+page.svelte: same fix - Both pages were stuck on «Загрузка...» due to mixed reactivity models ### Validation popover positioning - +page.svelte: pass trigger HTMLElement instead of event - DashboardHubModel.svelte.ts: toggleValidationPopover(HTMLElement), closeValidationPopover() - Added X close button + click-outside overlay + i18n ### Test fixes & integration tests - api.test.ts: mock setTraceId/getTraceId, +3 _captureTraceId tests - provider_config.integration.test.ts: handleDelete→promptDeleteProvider - DatasetPreview.test.ts: dashboards/ → ROUTES.dashboards - test_config_tab_form.svelte.js: +2 columns fetch guard tests (NEW) - admin-users.test.ts: +3 loading→table tests (NEW) - admin-roles.test.ts: +2 loading→table tests (NEW) ## Semantic curation - Removed @COMPLEXITY N from 6 route files + metrics.py (duplicate of [C:N]) - Added [C:N] to 2 orphan child contracts in metrics.py - Added [C:N] + @BRIEF to 4 frontend anchors - Fixed #region → # #region consistency in validation_tasks.py ## Verification - Backend: 608 pytest passed (0 failures) - Frontend: 2472 vitest passed (128 files, 0 failures) - Frontend build: ✓ built in 18s - Browser: dashboards, admin/users, admin/roles, validation popover — all green
103 lines
16 KiB
Markdown
103 lines
16 KiB
Markdown
#region FeatureSpec [C:3] [TYPE ADR] [SEMANTICS spec,requirements,feature,task,status,monitoring]
|
||
@BRIEF Feature specification — единый центр просмотра статусов всех фоновых задач superset-tools: переводы, миграции, бекапы, валидации, документация и прочие. Без привязки к реализации Python/Svelte.
|
||
|
||
## Navigation (DSA Indexer keywords)
|
||
@SEMANTICS: spec, requirements, feature, task, status, monitoring, dashboard
|
||
|
||
**Feature Branch**: `034-task-status-center`
|
||
**Created**: 2026-06-18 | **Status**: Draft
|
||
**Input**: "я хочу единый центр просмотра статусов всех задач - переводов миграций бекапов валидаций и прочих."
|
||
|
||
## Clarifications
|
||
|
||
### Session 2026-06-18
|
||
- Q: Центр статусов — новая страница или переработка существующей `/reports`? → A: Переработка `/reports` — добавляем сводную панель, real-time обновления и интеграцию с Task Drawer на существующей странице. Новый роут `/tasks` не создаётся. Старый режим «только история» остаётся доступным через фильтр.
|
||
- Q: Какие задачи видит каждая роль (admin, analyst, viewer) на `/reports`? → A: Admin — все задачи всех пользователей; analyst — свои задачи + задачи без владельца (системные); viewer — только задачи, запущенные им лично.
|
||
- Q: Показывать ли в сводке и списке задачи, удалённые retention-политиками? → A: Нет. Сводка и список — оперативная панель, показывающая только незаархивированные задачи. Архивные задачи исключены из выборки.
|
||
|
||
## User Scenarios
|
||
|
||
Each story is an independently testable unit. Prioritized P1 (MVP) → P2 → P3.
|
||
All stories share `@SEMANTICS` domain keywords from the feature header.
|
||
|
||
### Story 1 — Сводный обзор всех задач (P1)
|
||
|
||
**Why P1**: Существующая страница `/reports` показывает только исторические отчёты завершённых задач. Оператору нужна сводная картина «прямо сейчас»: сколько задач выполняется, ожидает, требует ввода — без переключения между страницами. Сводная панель добавляется поверх существующего списка отчётов на `/reports`.
|
||
|
||
**Independent Test**: Открыть страницу `/reports` → над списком отчётов появилась сводная панель с агрегированными счётчиками задач по статусам (pending, running, awaiting_input, success, failed) для каждого типа.
|
||
|
||
**Acceptance**:
|
||
1. **Given** в системе есть задачи разных типов (миграция выполняется, перевод ожидает, бекап завершён с ошибкой) **When** оператор открывает `/reports` **Then** он видит сводную панель над списком отчётов с агрегированными счётчиками по статусам и типам задач.
|
||
2. **Given** оператор находится на `/reports` **When** запускается новая задача или меняется статус существующей **Then** сводка обновляется в реальном времени (без ручной перезагрузки страницы).
|
||
3. **Given** в системе нет ни одной задачи **When** оператор открывает `/reports` **Then** сводная панель показывает нули по всем категориям, список отчётов пуст с пояснением «Нет задач».
|
||
|
||
---
|
||
|
||
### Story 2 — Детальный список задач с фильтрацией (P2)
|
||
|
||
**Why P2**: Сводка даёт общую картину, но оператору нужно найти конкретную задачу: «что там с миграцией дашборда X?», «какие переводы упали сегодня?», «показать только бекапы за последние сутки».
|
||
|
||
**Independent Test**: На странице `/reports` кликнуть по карточке сводки «Миграция / Выполняется (3)» → список отчётов фильтруется: только миграции в статусе running.
|
||
|
||
**Acceptance**:
|
||
1. **Given** в системе есть задачи всех типов **When** оператор применяет фильтр по типу задачи **Then** список обновляется и показывает только задачи выбранного типа.
|
||
2. **Given** в системе есть задачи в разных статусах **When** оператор применяет фильтр по статусу (только running + pending) **Then** список показывает только активные задачи.
|
||
3. **Given** оператор ввёл текст в строку поиска **When** текст совпадает с названием задачи или идентификатором дашборда/конфигурации **Then** список фильтруется по совпадению.
|
||
4. **Given** оператор выбрал временной диапазон «последний час» **When** задачи созданы более часа назад **Then** они не отображаются в списке.
|
||
|
||
---
|
||
|
||
### Story 3 — Быстрый переход к логам и результатам задачи (P3)
|
||
|
||
**Why P3**: Увидев, что задача упала или выполняется, оператор хочет немедленно посмотреть логи или результат — без поиска задачи на другой странице.
|
||
|
||
**Independent Test**: На странице `/reports` кликнуть на строку задачи со статусом FAILED → открывается Task Drawer с логами этой задачи.
|
||
|
||
**Acceptance**:
|
||
1. **Given** оператор на `/reports` видит задачу со статусом FAILED **When** он кликает по задаче **Then** открывается Task Drawer с логами задачи, курсор прокручивается к последней ошибке.
|
||
2. **Given** оператор на `/reports` видит задачу со статусом RUNNING **When** он кликает по задаче **Then** открывается Task Drawer и начинает показывать live-логи через WebSocket.
|
||
3. **Given** оператор навёлся на строку задачи **When** курсор находится над строкой **Then** появляется тултип с краткой информацией: тип, время запуска, длительность, ключевой параметр (например, название дашборда).
|
||
|
||
---
|
||
|
||
### Edge Cases
|
||
- **Массовое завершение задач**: Одновременно завершаются десятки задач (например, batch-перевод) → сводка и список должны корректно отобразить все изменения без зависания UI (debounce обновлений, batch-обработка WebSocket-событий).
|
||
- **Потеря WebSocket-соединения**: Соединение разрывается → индикатор состояния соединения в интерфейсе; автоматический reconnect с экспоненциальной задержкой; на время разрыва показывается «данные могут быть неактуальны».
|
||
- **Длительная загрузка истории**: В системе тысячи завершённых задач → пагинация и серверная фильтрация предотвращают загрузку полного списка; активные задачи всегда показываются первыми.
|
||
- **Задача с типом UNKNOWN**: Плагин не сопоставлен ни с одним известным типом → отображается с меткой «Неизвестный тип», не ломает фильтрацию.
|
||
- **Задача в статусе AWAITING_INPUT**: Задача ожидает ввода пользователя → отображается в сводке отдельной категорией «Ожидает ввода»; в списке — с визуальным индикатором и кнопкой «Ответить».
|
||
- **Отсутствие прав**: Пользователь с ролью viewer видит только свои задачи в списке и сводке; задачи других пользователей не отображаются. Попытка прямого доступа к чужой задаче через URL — 403.
|
||
- **Переключение между страницами**: Оператор уходит с `/reports` и возвращается → восстанавливается последний набор фильтров (из URL query params), сводка перезапрашивается.
|
||
- **Задачи, удалённые retention-политикой**: Задача очищена из БД → исчезает из сводки и списка при следующем обновлении данных. Оператору не нужно видеть «призраки» удалённых задач.
|
||
|
||
## Requirements
|
||
|
||
### Functional (IDs survive HCA 128× via hierarchical naming)
|
||
|
||
- **TSC-FR-001**: Страница `/reports` отображает сводную панель над списком отчётов с агрегированными счётчиками задач по статусам (pending, running, awaiting_input, success, failed) для каждого типа задач (перевод, миграция, бекап, валидация, документация, прочие).
|
||
- **TSC-FR-002**: Сводная панель обновляется в реальном времени через WebSocket без перезагрузки страницы.
|
||
- **TSC-FR-003**: Список отчётов (задач) отображается с пагинацией, сортировкой по времени создания (свежие сверху) и возможностью изменить сортировку.
|
||
- **TSC-FR-004**: Оператор может фильтровать список отчётов по: типу задачи, статусу, текстовому поиску (по названию задачи, идентификатору ресурса), временному диапазону. Клик по карточке сводки автоматически устанавливает соответствующий фильтр.
|
||
- **TSC-FR-005**: Каждая задача в списке показывает: тип (с иконкой), название/идентификатор, статус (с цветовой индикацией), время запуска, длительность, связанный ресурс (дашборд, датасет, конфигурация перевода).
|
||
- **TSC-FR-006**: Клик по задаче открывает Task Drawer с логами этой задачи через WebSocket (`/ws/logs/{task_id}`). Для завершённых задач — загружает архивные логи.
|
||
- **TSC-FR-007**: Задачи в статусе AWAITING_INPUT отображаются с визуальным индикатором и кнопкой «Ответить», открывающей форму ввода в Task Drawer.
|
||
- **TSC-FR-008**: Статус WebSocket-соединения отображается индикатором; при разрыве — автоматический reconnect с уведомлением оператора.
|
||
- **TSC-FR-009**: Страница `/reports` соблюдает RBAC: admin видит задачи всех пользователей; analyst видит свои задачи и системные задачи (без владельца); viewer видит только задачи, запущенные им лично. Сводная панель отражает только видимые пользователю задачи.
|
||
- **TSC-FR-010**: Активные задачи (running, pending, awaiting_input) всегда показываются в начале списка, независимо от выбранной сортировки.
|
||
|
||
### Key Entities
|
||
|
||
- **Task**: Фоновая задача, запущенная через любой плагин superset-tools. Ключевые атрибуты: идентификатор, тип (translate, migration, backup, validation, documentation, git, storage, debug, search, mapper, maintenance, clean_release, unknown), статус (PENDING, RUNNING, SUCCESS, FAILED, AWAITING_INPUT, AWAITING_MAPPING), время создания/запуска/завершения, связанный ресурс, идентификатор пользователя. Архивные задачи (удалённые retention-политикой) исключаются из сводки и списка.
|
||
- **TaskSummary**: Агрегированная сводка — срез количества задач по каждой комбинации (тип × статус). Обновляется в реальном времени.
|
||
- **TaskFilter**: Набор параметров фильтрации, применяемых к списку задач: типы, статусы, поисковый запрос, временной диапазон.
|
||
|
||
## Success Criteria
|
||
|
||
- **SC-001**: Оператор получает сводку по всем задачам не более чем за 1 секунду после открытия `/reports`.
|
||
- **SC-002**: Изменение статуса задачи отражается в сводке и списке не более чем через 2 секунды после события на backend (измеряется от отправки WebSocket-события до обновления DOM).
|
||
- **SC-003**: Оператор находит нужную задачу (фильтрация + поиск или клик по карточке сводки) не более чем за 2 действия после открытия `/reports`.
|
||
- **SC-004**: При 100+ одновременно активных задачах интерфейс остаётся отзывчивым (частота кадров не падает ниже 30 fps при прокрутке списка).
|
||
- **SC-005**: Восстановление WebSocket-соединения после разрыва происходит автоматически в течение 5 секунд с экспоненциальной задержкой.
|
||
|
||
#endregion FeatureSpec
|