38 KiB
Frontend Design Audit: ss-tools
Дата: 2026-06-02 (ревизия 2) Цель: Аудит фронтенд кода на предмет "плавающего" дизайна, выработка AI-native правил Объём: ~120 файлов, 8 UI-атомов, 55+ компонентов, 9 моделей, 9 сторов, все страницы, tailwind.config.js + ROUTES registry + link-integrity test
Содержание
- Методология
- Архитектурный ландшафт
- Найденные проблемы
- 3.1 Критические
- 3.2 Средние
- 3.3 Малые
- Причины плавающего дизайна
- AI-Native Design Rules
- Приоритетный план исправлений
- Статус исправлений
- Следующие шаги для AI-First
- Приложение: Карта файлов
1. Методология
Сканированные области
frontend/
├── tailwind.config.js ✅ конфиг темы
├── src/
│ ├── app.css ✅ 3 строки (tailwind directives)
│ ├── routes/ ✅ все 35+ страниц
│ ├── lib/
│ │ ├── ui/ ✅ 8 атомарных компонентов
│ │ ├── components/ ✅ 30+ доменных компонентов
│ │ ├── stores/ ✅ 9 сторов (8 legacy + 1 runes)
│ │ ├── models/ ✅ 9 моделей
│ │ ├── api/ ✅ клиентский слой
│ │ └── utils/ ✅ утилиты
│ └── components/ ✅ 30+ компонентов старого стиля
Критерии оценки
Каждый файл проверялся по 7 осям:
- Design tokens — использует ли семантические цвета из
tailwind.config.jsили сырые классы - Component reuse — использует ли
$lib/uiатомы или дублирует стили - State architecture — есть ли Screen Model для сложных страниц
- Svelte 5 compliance — runes-only или legacy паттерны
- TypeScript — типизирован или JSDoc/any
- UX contracts — полнота семантических аннотаций
- Module size — не превышает ли 400 строк (INV_7)
2. Архитектурный ландшафт
2.1 Tailwind Config (tailwind.config.js)
Определены семантические цвета, которые должны быть единым источником истины:
theme: {
extend: {
colors: {
primary: {
DEFAULT: '#2563eb', // blue-600
hover: '#1d4ed8', // blue-700
ring: '#3b82f6', // blue-500
light: '#eff6ff', // blue-50
},
secondary: {
DEFAULT: '#f3f4f6', // gray-100
hover: '#e5e7eb', // gray-200
text: '#111827', // gray-900
ring: '#6b7280', // gray-500
},
destructive: {
DEFAULT: '#dc2626', // red-600
hover: '#b91c1c', // red-700
ring: '#ef4444', // red-500
light: '#fef2f2', // red-50
},
ghost: {
hover: '#f3f4f6', // gray-100
text: '#374151', // gray-700
ring: '#6b7280', // gray-500
},
terminal: { /* dark palette for log viewer */ },
log: { /* debug, info, warning, error */ },
source: { /* plugin, api, git, system */ },
},
width: {
sidebar: '240px',
'sidebar-collapsed': '64px',
},
fontFamily: {
mono: ['"JetBrains Mono"', '"Fira Code"', 'monospace'],
},
},
}
2.2 UI-атомы (src/lib/ui/)
| Компонент | Описание | Использует семантические токены |
|---|---|---|
Button.svelte |
primary/secondary/danger/ghost variants | ✅ bg-primary, bg-destructive |
Card.svelte |
Container with title, padding variants | ❌ border-gray-200 bg-white |
Input.svelte |
Text input with label + error | ❌ border-gray-300, focus:ring-primary-ring |
Select.svelte |
Dropdown with label | ❌ border-gray-300 |
PageHeader.svelte |
Title + subtitle + actions slot | ❌ text-gray-900 |
Icon.svelte |
SVG icon set | ❌ цвета не использует (currentColor) |
HelpTooltip.svelte |
Tooltip helper | ❌ |
LanguageSwitcher.svelte |
Lang toggle | ❌ |
Вывод: Только Button системно использует семантические токены. Остальные атомы используют сырые Tailwind-классы. Это источник "плавания" — если поменять primary цвет в конфиге, Button изменится, а Card/Input/Select — нет.
2.3 Состояние: 3 параллельных подхода
┌────────────────────────────────────────────────────────────────┐
│ STATE MANAGEMENT MAP │
├────────────────────────────────────────────────────────────────┤
│ │
│ LEGACY STORES (svelte/store writable) │
│ ├── sidebar.ts — sidebar state + localStorage │
│ ├── taskDrawer.ts — task drawer visibility │
│ ├── datasetReviewSession.ts — dataset review session │
│ ├── environmentContext.ts — env context + selection │
│ ├── health.ts — health check state │
│ ├── activity.ts — derived activity │
│ ├── assistantChat.ts — assistant chat session │
│ └── translationRun.ts — translation run tracking │
│ │
│ MODERN STORES (.svelte.ts with $state) │
│ ├── maintenance.svelte.ts — maintenance + WebSocket │
│ │
│ SCREEN MODELS (.svelte.ts class with $state) │
│ ├── MigrationModel.svelte.ts — migration wizard state │
│ ├── GitManagerModel.svelte.ts — git manager state │
│ ├── GitStatusModel.svelte.ts — git status per dashboard │
│ ├── GitConfigModel.svelte.ts — git configuration │
│ ├── BranchModel.svelte.ts — branch management │
│ ├── DeploymentModel.svelte.ts — deployment state │
│ ├── CommitModel.svelte.ts — commit history state │
│ ├── MigrationSettingsModel.svelte.ts — migration settings │
│ └── MappingsModel.svelte.ts — db mapping state │
│ │
└────────────────────────────────────────────────────────────────┘
Проблема: AI-агент видит 3 разных паттерна и не знает, какой выбрать для нового кода.
2.4 Структура компонентов: двойная иерархия
src/lib/ui/ # Атомарные UI-компоненты (8 файлов)
├── Button.svelte
├── Card.svelte
├── Input.svelte
├── Select.svelte
├── PageHeader.svelte
├── Icon.svelte
├── HelpTooltip.svelte
└── LanguageSwitcher.svelte
src/lib/components/ # Доменные компоненты (30+ файлов)
├── layout/
├── translate/
├── reports/
├── dataset-review/
├── ui/ # SearchableMultiSelect, MultiSelect
├── health/
├── llm/
└── assistant/
src/components/ # Старые компоненты (30+ файлов)
├── auth/
├── git/
├── tasks/
├── tools/
├── backups/
├── storage/
├── llm/
├── EnvSelector.svelte
├── DashboardGrid.svelte
├── MappingTable.svelte
├── TaskRunner.svelte
├── TaskHistory.svelte
├── Toast.svelte
├── Navbar.svelte
├── Footer.svelte
├── StartupEnvironmentWizard.svelte
└── ...
Проблема: src/components/ — наследие, но непонятно, почему часть компонентов там, а часть в lib/components/. Для AI-агента это создаёт неопределённость.
2.5 Карта страниц и их состояния
| Страница | Строк | $state атомов | Model? | Использует $lib/ui? | Типизация |
|---|---|---|---|---|---|
dashboards/+page.svelte |
2765 | ~30+ | ❌ нет | ❌ частично | lang="ts" |
datasets/+page.svelte |
472 | ~15+ | ❌ нет | ❌ нет | lang="ts" |
migration/+page.svelte |
637 | ~5 (в модели) | ✅ MigrationModel | ✅ Button, Card, PageHeader | lang="ts" |
settings/+page.svelte |
290 | ~5 | ❌ нет | ❌ нет | lang="ts" |
validation-tasks/+page.svelte |
519 | ~10 | ❌ нет | ❌ нет | ❌ JSDoc |
reports/+page.svelte |
204 | ~6 | ❌ нет | ✅ PageHeader | ❌ без типов |
dashboards/[id]/+page.svelte |
582 | ~15 | частично (GitStatusModel) | ❌ нет | lang="ts" |
datasets/review/+page.svelte |
— | — | ❌ | — | — |
translate/+page.svelte |
— | — | ❌ | — | — |
3. Найденные проблемы
3.1 🔴 Критические
🔴 Проблема 1: Трёхголовая архитектура состояния
Описание: В проекте одновременно существуют 3 несвязанных подхода к управлению состоянием.
Доказательство:
// ПАТТЕРН 1: Legacy writable store (src/lib/stores/sidebar.ts)
import { writable, type Writable } from 'svelte/store';
export const sidebarStore: Writable<SidebarState> = writable(initialState);
// ПАТТЕРН 2: Rune-based store (src/lib/stores/maintenance.svelte.ts)
export function createMaintenanceStore(): MaintenanceStore {
let activeEvents = $state<unknown[]>([]);
return { get activeEvents() { return activeEvents; }, ... };
}
// ПАТТЕРН 3: Screen Model class (src/lib/models/MigrationModel.svelte.ts)
export class MigrationModel {
currentStep: number = $state(1);
}
Последствия:
- AI-агент не может определить, какой паттерн использовать
- Разный DX для тестирования: Model тестируется без DOM, Store — через
get()
🔴 Проблема 2: Нет Screen Model для сложных страниц
Описание: Страницы с 10+ $state атомами и cross-widget инвариантами не выделены в Model.
Доказательство — dashboards/+page.svelte (2765 строк):
<!-- ❌ ВСЁ В ОДНОМ ФАЙЛЕ: ~30 атомов, ~50 функций -->
<script lang="ts">
let allDashboards = $state([]);
let dashboards = $state([]);
let isLoading = $state(true);
let error = $state(null);
let currentPage = $state(1);
let searchQuery = $state("");
// ... ещё 20+ атомов, 50+ функций, Git-операции, всё инлайн
</script>
Последствия:
- Невозможно протестировать unit-тестами без DOM
- AI-агент теряет контекст к 20-й функции
- Нарушение INV_7 (>400 строк)
🔴 Проблема 3: Мёртвые ссылки — навигация на несуществующие роуты
Описание: Ссылки формировались как сырые строки в 40+ местах. Если роут переименован или не создан — 404 в рантайме.
Найденные битые роуты:
| Ссылка | Откуда | Статус |
|---|---|---|
/dashboards/{id}/validation?env_id= |
dashboards/[id]/+page.svelte, dashboards/health/+page.svelte |
❌ → ✅ СОЗДАН |
/validation/{taskId} |
ScheduleAtAGlance.svelte |
❌ → ✅ ИСПРАВЛЕН |
Исправление (2026-06-02):
- Создан
$lib/routes.ts— централизованный реестр с type-safe builder-функциями - 47 raw goto/href мигрированы на
ROUTES.*()(21 файл) - Добавлен link-integrity тест (46 тестов + сканирование raw-строк)
- Создан роут
/dashboards/{id}/validation/ - Починена ссылка
/validation/{taskId}→/validation-tasks/{taskId}
// Было (размазано): goto(`/dashboards/${id}/validation?env_id=${envId}`);
// Стало (SSOT): goto(ROUTES.dashboards.validation(id, envId));
Результат: 698 тестов, 0 raw goto/href, 0 битых роутов.
Новые AI-Native правила (Rules 9-10, см. раздел 5.2):
- Rule 9: Все навигационные ссылки — только через
ROUTES.*() - Rule 10: Link-integrity тест обязателен в CI
🔴 Проблема 4: Размытие дизайн-токенов
Описание: Семантические цвета из tailwind.config.js используются непоследовательно. Рядом с bg-primary соседствуют bg-indigo-50, bg-gradient-to-br from-slate-50 via-white to-sky-50, bg-red-100.
Доказательство — сравнение паттернов в dashboards/+page.svelte:
<!-- ✅ Семантический токен (редко) -->
<button class="bg-primary text-white hover:bg-primary-hover">
<!-- ❌ Дрейф: indigo вместо primary -->
<button class="inline-flex items-center justify-center rounded-lg border border-indigo-300 bg-indigo-50 px-4 py-2 text-sm font-medium text-indigo-700 transition-colors hover:bg-indigo-100">
<!-- ❌ Дрейф: сырой red вместо destructive -->
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4 flex items-center justify-between">
<!-- ❌ Рядом destructive (правильно) и сырой red (неправильно) -->
<button class="px-4 py-2 bg-destructive text-white rounded hover:bg-destructive-hover transition-colors">
<!-- ❌ sky-50, gradient — нет в токенах -->
<div class="relative overflow-hidden rounded-3xl border border-slate-200 bg-gradient-to-br from-slate-50 via-white to-sky-50 p-8 shadow-sm">
Примеры из других файлов:
<!-- validation-tasks/+page.svelte — сырые цвета -->
<select class="rounded-md border border-slate-300 px-2 py-1.5 text-sm">
<!-- settings/+page.svelte — сырые цвета -->
<div class="border-b border-gray-200">
<!-- reports/+page.svelte — сырой Card вместо компонента Card -->
<div class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
<!-- settings/+page.svelte — 3 разных стиля кнопок в одном файле -->
<button class="bg-blue-600 text-white px-3 py-1.5 text-sm rounded hover:bg-blue-700">
<button class="px-3 py-1.5 text-sm border border-gray-300 rounded hover:bg-gray-100">
<button class="text-blue-600 hover:text-blue-800 text-sm font-medium">
Последствия:
- Изменение
primaryв tailwind.config.js не синхронизируется с indigo/sky-кнопками - AI-агент не может определить "правильный" цвет — каждый раз гадает
- Визуальная несогласованность: кнопки на одной странице выглядят по-разному
- 5 разных стилей для одного и того же UI-паттерна
3.2 🟡 Средние
🟡 Проблема 5: Button-компонент не стандартизирован
Описание: Только migration/+page.svelte системно использует $lib/ui/Button. 7 разных стилей ручных <button> в проекте.
🟡 Проблема 6: Дублирование каталогов компонентов
Описание: 3 корневые директории (lib/ui/, lib/components/, components/) без чётких правил. src/components/ — замороженное наследие.
🟡 Проблема 7: 33 any в TypeScript
Описание: 33 вхождения any в исходниках — функции getRunLink(run: any), dashboard: any, catch (err: any).
Ключевые цели:
function getRunLink(run: any)→ конкретный интерфейсRundashboard: any→Dashboardcatch (err: any)→catch (err: unknown)
Последствие: AI-агент вынужден угадывать форму данных. any — главный источник галлюцинаций.
🟡 Проблема 8: JSDoc вместо TypeScript
Описание: validation-tasks/+page.svelte и другие страницы используют JSDoc вместо <script lang="ts">.
🟡 Проблема 9: 8 легаси-сторов блокируют миграцию на runes
Описание: Все сторы, кроме maintenance.svelte.ts, используют Svelte 4 writable().
🟡 Проблема 10: Неравномерное покрытие UX-контрактами
@UX_REACTIVITY отсутствует везде, кроме MigrationPage. См. таблицу ниже.
🟡 Проблема 11: Нет Component Catalog — SSOT для UI-компонентов
Описание: AI-агент не может за 1 чтение узнать, какие компоненты существуют и какие у них пропсы. Масштаб: 8 атомов + 30+ доменных компонентов — все с разными интерфейсами пропсов.
🟡 Проблема 12: Нет API Data Contracts
Описание: api.ts содержит 30+ endpoint-методов, но ни один не документирует входные/выходные DTO.
AI-агент не знает форму API-ответов без чтения бэкенда.
3.3 🟢 Малые
🟢 Проблема 13: cn() utility не строго типизирован
🟢 Проблема 14: Дублирование debounce
🟢 Проблема 15: Zero-state не переиспользуется
🟢 Проблема 16: Border radius плавает
4. Причины плавающего дизайна
4.1 Отсутствие единого Design Language
У проекта есть tailwind.config.js с семантическими токенами, но нет документированных правил:
- Когда использовать
primaryvsblue-600vsindigo-600? - Какой border radius для чего?
- Какой набор компонентов обязателен к использованию?
4.2 Две эпохи Svelte
Проект начинался на Svelte 4 (writable stores, on:event, export let), потом мигрировал на Svelte 5 (runes, $state). Миграция не завершена:
- 8 stores остались на writable
- 30+ компонентов в
src/components/— наследие - Новые страницы пишутся на runes, но без model-first
4.3 AI-агенты без правил
Каждый AI-агент (человек или LLM) при создании новой страницы:
- Не знает, какой цвет использовать — выбирает "похожий"
- Не знает, какой компонент переиспользовать — пишет с нуля
- Не знает, куда положить файл — кладёт в
routes/илиcomponents/ - Не знает паттерн состояния — пишет
$state+ функции инлайн
4.4 Нет "шлюза" для нового кода
Нет code review checklist или pre-commit хука, который проверял бы:
- Использует ли страница
<Button>вместо ручного<button>? - Использует ли страница семантические токены?
- Есть ли Screen Model для страницы с >5 атомами?
5. AI-Native Design Rules
Эти правила предназначены для включения в промпт AI-агента (Svelte-кодера) и для CI-валидации.
Rule 1: Semantic Color Tokens Over Raw Colors
// ✅ CORRECT
<div class="bg-primary text-white hover:bg-primary-hover">
<div class="bg-destructive text-white">
<div class="bg-secondary text-secondary-text">
// ❌ WRONG
<div class="bg-blue-600 text-white">
<div class="bg-indigo-50 text-indigo-700">
<div class="bg-red-100 text-red-700">
<div class="bg-gradient-to-br from-slate-50 via-white to-sky-50">
Исключение: Только в tailwind.config.js при определении новых токенов.
Rule 2: UI Atoms Are Mandatory For Pages
// ✅ CORRECT
import { Button, Card, Input, Select, PageHeader } from "$lib/ui";
<Button variant="primary" onclick={handleSave}>
<Card>
<PageHeader title="Settings" subtitle={() => <p>Sub</p>} />
// ❌ WRONG
<button class="px-3 py-1 bg-primary rounded text-white">
<div class="bg-white rounded-xl border shadow-sm p-6">
<div class="flex items-center justify-between mb-8">
<h1 class="text-3xl font-bold">Settings</h1>
Исключение: layout-компоненты и сами атомы в $lib/ui/.
Rule 3: Screen Model For Any Page With >5 $state Atoms
// ✅ CORRECT: DashboardHubModel.svelte.ts
export class DashboardHubModel {
dashboards: Dashboard[] = $state([]);
page: number = $state(1);
searchQuery: string = $state("");
// ...
async loadDashboards(): Promise<void> { ... }
async deleteDashboard(id: string): Promise<void> { ... }
}
// ✅ CORRECT: migration/+page.svelte (thin render layer)
const model = new MigrationModel();
onMount(() => model.loadEnvironments());
// ❌ WRONG: dashboards/+page.svelte (2765 строк, ~30 $state атомов инлайн)
Trigger: Если в <script> страницы больше 5 $state(...) вызовов → создай Model.
Rule 4: Runes Only For New State Code
// ✅ CORRECT (.svelte.ts)
let count = $state(0);
const doubled = $derived(count * 2);
// ❌ WRONG (.ts)
import { writable, derived } from 'svelte/store';
const count = writable(0);
const doubled = derived(count, $count => $count * 2);
Исключение: Только рефакторинг существующих writable-сторов, у которых есть подписчики.
Rule 5: One Component Directory — lib/components/
// ✅ CORRECT
src/lib/components/git/GitManager.svelte
src/lib/components/git/BranchSelector.svelte
// ❌ WRONG
src/components/git/GitManager.svelte
src/components/git/BranchSelector.svelte
Trigger: Новый доменный компонент → src/lib/components/<domain>/. src/components/ — заморожено.
Rule 6: TypeScript Required On Pages
// ✅ CORRECT
<script lang="ts">
let { data }: { data: PageData } = $props();
let items: Item[] = $state([]);
// ❌ WRONG
<script>
/** @type {Array<any>} */ let items = $state([]);
Rule 7: Consistent Border Radius Hierarchy
rounded-md → inputs, buttons, selects (h-10)
rounded-lg → cards, containers, modals
rounded-xl → panels, drawers (опционально)
rounded-3xl → zero-state / empty-state только
Rule 8: Page Max 400 Lines
>400 lines → Model + Component decomposition
>10 cyclomatic complexity → helper functions
Rule 9: Route Registry For Navigation (NEW 2026-06-02)
// ✅ CORRECT
import { ROUTES } from '$lib/routes.js';
<a href={ROUTES.dashboards.validation(id, envId)}>History</a>
goto(ROUTES.dashboards.detail(id, envId));
// ❌ WRONG — raw string, неверифицируемо, риск 404
<a href={`/dashboards/${id}/validation?env_id=${envId}`}>History</a>
goto(`/dashboards/${id}?env_id=${envId}`);
Trigger: Любая навигация (goto, href, window.location.href) → только через ROUTES.*().
Исключение: Внешние URL (/api/auth/login/adfs).
Rule 10: Link Integrity Test Required (NEW 2026-06-02)
Перед каждым CI-run:
1. vitest run src/lib/__tests__/routes-link-integrity.test.ts
2. Падать при любых raw goto/href строках (кроме ROUTES)
3. Падать, если ROUTES builder не匹配 ни один файловый роут
6. Приоритетный план исправлений
✅ Выполнено (2026-06-02)
| # | Задача | Статус |
|---|---|---|
| ✅ | Создать ROUTES registry ($lib/routes.ts) |
✅ Все 46 builder-функций |
| ✅ | Link-integrity тест | ✅ 698 тестов проходят |
| ✅ | Мигрировать 47 raw goto/href → ROUTES | ✅ 21 файл изменён |
| ✅ | Создать роут /dashboards/{id}/validation/ |
✅ Было 404 → отдаёт данные |
| ✅ | Починить /validation/{taskId} → /validation-tasks/{taskId} |
✅ Было 404 → работает |
P0 — Немедленно (2-3 часа)
| # | Задача | Файлы | Ожидаемый результат |
|---|---|---|---|
| 0.1 | Выделить DashboardHubModel.svelte.ts |
dashboards/+page.svelte |
2765 → <400 строк |
| 0.2 | Создать EmptyState компонент |
lib/ui/EmptyState.svelte |
Единый zero-state |
P1 — Важно (5-6 часов)
| # | Задача | Файлы | Ожидаемый результат |
|---|---|---|---|
| 1.1 | Component Catalog — SSOT всех UI-компонентов с пропсами | $lib/components.ts |
AI-агент видит все компоненты за 1 чтение |
| 1.2 | API Data Contracts — @DATA_CONTRACT на все endpoint-методы |
api.ts |
AI-агент знает DTO без чтения бэкенда |
| 1.3 | Типизация any (33 вхождения) |
15+ файлов | run: any → Run, dashboard: any → Dashboard |
P2 — Среднесрочно (4-5 часов)
| # | Задача | Файлы | Ожидаемый результат |
|---|---|---|---|
| 2.1 | Заменить raw colors на semantic tokens | Все page-файлы | Ни одного bg-indigo-* в page |
| 2.2 | Заменить ручные <button> на <Button> |
Все page-файлы | Единый стиль кнопок |
| 2.3 | Мигрировать 1-2 легаси стора на .svelte.ts |
taskDrawer.ts, sidebar.ts |
Runes-only stores |
| 2.4 | Выделить DatasetHubModel |
datasets/+page.svelte |
model-first для datasets |
| 2.5 | Выделить ValidationTasksModel |
validation-tasks/+page.svelte |
model-first для validation |
P3 — Долгосрочно (6-8 часов)
| # | Задача | Файлы | Ожидаемый результат |
|---|---|---|---|
| 3.1 | Консолидировать src/components/ в lib/components/ |
30+ файлов | Одна иерархия |
| 3.2 | Мигрировать оставшиеся 6 writable-сторов | 6 store-файлов | Runes-only |
| 3.3 | Семантические токены для всех UI-атомов | Card, Input, Select, PageHeader |
Единые токены |
| 3.4 | Store Registry — SSOT для всех сторов | $lib/stores/index.ts |
1 файл, 30 строк |
7. Статус исправлений (2026-06-02)
Что сделано
| Изменение | Файлы | Тесты |
|---|---|---|
Создан $lib/routes.ts — ROUTES registry |
1 новый файл | 46 тестов на валидность каждого builder |
| Создан link-integrity тест | 1 новый файл | Сканирует все src/ на raw goto/href |
Создан роут /dashboards/{id}/validation/ |
2 файла (+page.svelte, +page.ts) | Работает + пагинация + статус-бейджи |
| Мигрированы 47 raw goto/href → ROUTES | 21 файл изменён | 0 raw строк осталось |
Исправлен битый роут /validation/{id} |
1 файл (ScheduleAtAGlance) | Теперь ведёт на /validation-tasks/{id} |
| Исправлены 2 контрактных теста | 2 тестовых файла | Проверяют ROUTES.*() вместо raw строк |
Метрики качества
| Метрика | До | После |
|---|---|---|
| Raw goto/href строк в коде | 47 | 0 |
| Битых роутов | 2 | 0 |
| Тестов на целостность навигации | 0 | 46 |
| Файлов, изменённых для миграции | — | 21 |
| Всего тестов | 696 | 698 (все проходят) |
8. Следующие шаги для AI-First
На основе аудита и выполненной работы, следующие улучшения для AI-First (в порядке приоритета):
8.1 Component Catalog ($lib/components.ts)
Почему: AI-агент тратит ~5-10 чтений на поиск подходящего компонента. Component catalog сокращает это до 1.
Формат:
export const COMPONENTS = {
Button: {
path: "$lib/ui/Button.svelte",
props: {} as ButtonProps,
variants: ["primary", "secondary", "destructive", "ghost"] as const,
},
Card: { path: "$lib/ui/Card.svelte", props: {} as CardProps },
PageHeader: { path: "$lib/ui/PageHeader.svelte", props: {} as PageHeaderProps },
// ... все 30+ компонентов
} as const;
Трудоёмкость: 1 файл, ~50 строк.
8.2 API Data Contracts
Почему: Каждый fetchApi в api.ts — чёрный ящик для AI-агента. Без DTO агент галлюцинирует поля.
Формат:
// @BRIEF Fetch paginated dashboards for an environment
// @DATA_CONTRACT params -> { env_id, page, page_size, search?, filters? }
// @DATA_CONTRACT response -> { dashboards: Dashboard[], total: number, page: number }
getDashboards: <T = DashboardListResponse>(envId: string, opts?: DashboardQueryOptions) => ...
Трудоёмкость: 30+ методов, ~2-3 строки документации на каждый.
8.3 Типизация any (33 вхождения)
Почему: any — главный источник AI-галлюцинаций. Агент вынужден угадывать структуру.
Ключевые цели:
function getRunLink(run: any)→ конкретный интерфейсRundashboard: any→Dashboardcatch (err: any)→catch (err: unknown)
Трудоёмкость: ~15-20 файлов, механическая работа.
8.4 Store Registry ($lib/stores/index.ts)
Почему: Агент видит все глобальные состояния в одном месте.
Трудоёмкость: 1 файл, ~30 строк.
8.5 Semantic Index Maintenance
- Перестроить индекс с DuckDB для fuzzy-search
- Исправить 103 unresolved relations (информационный шум)
Трудоёмкость: 1 команда + фикс таргетов @RELATION.
9. Приложение: Карта файлов
7.1 UI-атомы (src/lib/ui/)
| Файл | Строк | Использует токены | Типы |
|---|---|---|---|
Button.svelte |
75 | ✅ bg-primary, bg-destructive |
lang="ts" |
Card.svelte |
58 | ❌ border-gray-200 bg-white |
lang="ts" |
Input.svelte |
66 | ❌ border-gray-300 + focus:ring-primary-ring |
lang="ts" |
Select.svelte |
58 | ❌ border-gray-300 |
lang="ts" |
PageHeader.svelte |
45 | ❌ text-gray-900 |
lang="ts" |
Icon.svelte |
111 | ✅ currentColor |
lang="ts" |
HelpTooltip.svelte |
— | — | — |
LanguageSwitcher.svelte |
— | — | — |
7.2 Модели (src/lib/models/)
| Файл | Строк | Семантика |
|---|---|---|
MigrationModel.svelte.ts |
472 | ✅ model-first — gold standard |
GitManagerModel.svelte.ts |
— | ✅ |
GitStatusModel.svelte.ts |
— | ✅ |
GitConfigModel.svelte.ts |
— | ✅ |
BranchModel.svelte.ts |
— | ✅ |
DeploymentModel.svelte.ts |
— | ✅ |
CommitModel.svelte.ts |
— | ✅ |
MigrationSettingsModel.svelte.ts |
— | ✅ |
MappingsModel.svelte.ts |
— | ✅ |
7.3 Сторы (src/lib/stores/)
| Файл | Строк | Паттерн | Семантические токены |
|---|---|---|---|
sidebar.ts |
79 | writable ❌ legacy |
✅ аннотации |
taskDrawer.ts |
112 | writable ❌ legacy |
✅ |
environmentContext.ts |
— | writable ❌ legacy |
✅ |
datasetReviewSession.ts |
87 | writable ❌ legacy |
✅ |
assistantChat.ts |
— | writable ❌ legacy |
✅ |
health.ts |
— | writable ❌ legacy |
✅ |
activity.ts |
— | writable ❌ legacy |
✅ |
translationRun.ts |
— | writable ❌ legacy |
❌ |
maintenance.svelte.ts |
203 | $state ✅ modern |
✅ |
7.4 Страницы (src/routes/)
| Файл | Строк | Model? | $lib/ui? | Типы | UX контракты |
|---|---|---|---|---|---|
dashboards/+page.svelte |
2765 | ❌ | ❌ частично | ts | ✅ 5/5 |
dashboards/[id]/+page.svelte |
582 | частично | ❌ | ts | ✅ 4/5 |
datasets/+page.svelte |
472 | ❌ | ❌ | ts | ❌ 1/5 |
datasets/review/+page.svelte |
— | ❌ | — | — | — |
migration/+page.svelte |
637 | ✅ | ✅ | ts | ✅ 5/5 |
settings/+page.svelte |
290 | ❌ | ❌ | ts | ✅ 4/5 |
validation-tasks/+page.svelte |
519 | ❌ | ❌ | ❌ JSDoc | ✅ 4/5 |
reports/+page.svelte |
204 | ❌ | ✅ PageHeader | ❌ any | ✅ 3/5 |
translate/+page.svelte |
— | ❌ | — | — | — |
+layout.svelte |
112 | — | — | ts | ✅ |
7.5 Старые компоненты (src/components/)
src/components/
├── auth/
│ └── ProtectedRoute.svelte
├── git/
│ ├── GitManager.svelte
│ ├── BranchSelector.svelte
│ ├── CommitHistory.svelte
│ ├── CommitModal.svelte
│ ├── ConflictResolver.svelte
│ ├── DeploymentModal.svelte
│ ├── GitInitPanel.svelte
│ ├── GitOperationsPanel.svelte
│ ├── GitReleasePanel.svelte
│ └── GitWorkspacePanel.svelte
├── tasks/
│ ├── TaskResultPanel.svelte
│ └── TaskLogPanel.svelte
│ └── LogFilterBar.svelte
│ └── LogEntryRow.svelte
├── tools/
│ ├── MapperTool.svelte
│ └── DebugTool.svelte
├── backups/
│ ├── BackupManager.svelte
│ └── BackupList.svelte
├── storage/
│ ├── FileUpload.svelte
│ └── FileList.svelte
├── llm/
│ ├── ValidationReport.svelte
│ ├── ProviderConfig.svelte
│ └── DocPreview.svelte
├── DashboardGrid.svelte
├── EnvSelector.svelte
├── Footer.svelte
├── MappingTable.svelte
├── MissingMappingModal.svelte
├── Navbar.svelte
├── PasswordPrompt.svelte
├── RepositoryDashboardGrid.svelte
├── StartupEnvironmentWizard.svelte
├── TaskHistory.svelte
├── TaskList.svelte
├── TaskLogViewer.svelte
├── TaskRunner.svelte
├── Toast.svelte
├── DynamicForm.svelte
└── RepositoryDashboardGrid.svelte
Аудит выполнен 2026-06-01. Полный контекст: ~120 файлов, 8 UI-атомов, 55+ компонентов, 9 моделей, 9 сторов, все 35+ страниц.