Files
ss-tools/specs/034-task-status-center/spec.md
busya 3133e50645 perf: fix translate deadlock, speed, trace_id, UI bugs — fullstack patch
## 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
2026-06-18 23:54:57 +03:00

103 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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