Files
ss-tools/frontend/ai-native-design-audit.md
2026-06-02 11:46:00 +03:00

38 KiB
Raw Blame History

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


Содержание

  1. Методология
  2. Архитектурный ландшафт
  3. Найденные проблемы
  4. Причины плавающего дизайна
  5. AI-Native Design Rules
  6. Приоритетный план исправлений
  7. Статус исправлений
  8. Следующие шаги для AI-First
  9. Приложение: Карта файлов

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 осям:

  1. Design tokens — использует ли семантические цвета из tailwind.config.js или сырые классы
  2. Component reuse — использует ли $lib/ui атомы или дублирует стили
  3. State architecture — есть ли Screen Model для сложных страниц
  4. Svelte 5 compliance — runes-only или legacy паттерны
  5. TypeScript — типизирован или JSDoc/any
  6. UX contracts — полнота семантических аннотаций
  7. 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) → конкретный интерфейс Run
  • dashboard: anyDashboard
  • catch (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 с семантическими токенами, но нет документированных правил:

  • Когда использовать primary vs blue-600 vs indigo-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) при создании новой страницы:

  1. Не знает, какой цвет использовать — выбирает "похожий"
  2. Не знает, какой компонент переиспользовать — пишет с нуля
  3. Не знает, куда положить файл — кладёт в routes/ или components/
  4. Не знает паттерн состояния — пишет $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).


Перед каждым 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: anyRun, dashboard: anyDashboard

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) → конкретный интерфейс Run
  • dashboard: anyDashboard
  • catch (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+ страниц.