# UX Reference: LLM Table Translation Service

**Feature Branch**: `028-llm-datasource-supeset`  
**Created**: 2026-05-08  
**Status**: Draft

## 1. User Persona & Context

*   **Primary user**: Analytics engineer or localization specialist who needs to translate column values from a Superset datasource into another language and write the results into a materialized target table.
*   **Secondary user**: Data steward who audits past translation runs for quality and compliance.
*   **What is the user trying to achieve?**: Convert reference data (product names, category labels, UI strings) stored in Superset-connected tables into a target language using an LLM, with full control over which columns provide translation context, how rows are matched to the target table, and what gets inserted.
*   **Mindset**: The user knows their data and knows which language they need. They want the LLM to handle the mechanical translation work but they need to review quality before committing. They do not trust translations enough to let them flow unattended into production tables.
*   **Context of use**:
    *   Preparing localized reference data for a BI dashboard rollout in a new language region.
    *   Translating product catalog descriptions stored in a Superset-visible database.
    *   Generating INSERT statements for a materialized translation table that feeds downstream reports.
    *   Reviewing and approving batches of translations before they become visible to end users.

## 2. UX Principles

*   **Preview gates manual runs, schedules bypass after first success**: Manual runs require preview acceptance. Scheduled runs may bypass preview only after at least one successful manual run with the same effective configuration.
*   **Context is king**: The system must make it easy to add relevant context columns that help the LLM produce better translations, and must show that context in the preview.
*   **Traceability**: Every inserted translation must be traceable back to its source row, the prompt used, the key values, and the approval decision.
*   **Graceful degradation**: When the LLM fails, the system preserves progress and enables targeted retry—never force the user to restart from scratch.
*   **Cost awareness**: Before any full batch execution, the user must see an estimated token count and cost so they can make informed decisions about batch size and scope.

## 3. Core Interaction Flows

### Flow A: Job Configuration (Happy Path)

```
1. User navigates to "Translation Jobs" section in the frontend.
2. User clicks "New Translation Job".
3. System presents a configuration form:

   ┌─────────────────────────────────────────────────────┐
   │  New Translation Job                                │
   │                                                     │
   │  Source Datasource:  [▼ Select Superset datasource] │
   │                                                     │
   │  ── Column Mapping ──                               │
   │  Translation column:  [▼ product_name         ]     │
   │  Context columns:     [✓ category_name       ]     │
   │                       [✓ product_description ]     │
   │                       [ ] supplier_name       ]     │
   │                                                     │
   │  ── Key Columns ──                                  │
   │  Key column 1:        [▼ product_id           ]     │
   │  [+ Add key column]                                 │
   │                                                     │
   │  ── Target ──                                       │
   │  Target table:        [ products_i18n________ ]     │
   │  Target column:       [ translated_name______ ]     │
   │                                                     │
   │  ── LLM Settings ──                                 │
   │  Target language:     [▼ Russian              ]     │
   │  Prompt template:     [ Edit default template  ]    │
   │  Batch size:          [ 50                    ]     │
   │                                                     │
   │  [Save Configuration]  [Save & Preview]             │
   └─────────────────────────────────────────────────────┘

4. User selects datasource → columns auto-populate from schema.
5. User picks translation column, context columns, key columns.
6. User specifies target table and target column.
7. System validates column existence and key compatibility on save.
8. Configuration saved → job appears in job list with status "Configured".
```

### Flow B: Preview & Approve

```
1. User opens a saved translation job and clicks "Preview".

2. System shows progress:
   ┌─────────────────────────────────────────────────────┐
   │  Translation Preview — products_i18n                │
   │  Sample: 10 rows  │  Estimated full: 1,243 rows     │
   │                                                     │
   │  ┌───────────────────────────────────────────────┐  │
   │  │ # │ Source (product_name) │ Context           │  │
   │  │   │                       │ (category_name)   │  │
   │  ├───┼───────────────────────┼───────────────────┤  │
   │  │ 1 │ "Wireless Mouse"      │ "Accessories"     │  │
   │  │   │ → "Беспроводная мышь" │                   │  │
   │  │   │ [Approve] [Edit] [Reject]                 │  │
   │  ├───┼───────────────────────┼───────────────────┤  │
   │  │ 2 │ "Gaming Keyboard"     │ "Accessories"     │  │
   │  │   │ → "Игровая клавиатура"│                   │  │
   │  │   │ [Approve] [Edit] [Reject]                 │  │
   │  └───┴───────────────────────┴───────────────────┘  │
   │                                                     │
   │  [Approve All]  [Re-run Preview]  [Start Full Run]  │
   └─────────────────────────────────────────────────────┘

3. User reviews each translation, clicks [Approve] for good ones,
   [Edit] to correct, or [Reject] to exclude.

4. Edited values are highlighted differently from LLM-generated values.

5. When satisfied, user clicks [Start Full Run].

6. System shows cost/duration estimate:
   ┌─────────────────────────────────────────────────────┐
   │  ⚠ Confirm Full Translation Run                     │
   │                                                     │
   │  Rows to translate:   1,243                          │
   │  Estimated tokens:    ~45,000                        │
   │  Estimated cost:      ~$0.09 (GPT-4o-mini)           │
   │  Batches:             25 × 50 rows                  │
   │                                                     │
   │  [Cancel]  [Confirm & Run]                          │
   └─────────────────────────────────────────────────────┘
```

### Flow C: Execution & Superset API Insert

```
1. User confirms full run.

2. System shows live progress with two phases:
   ┌─────────────────────────────────────────────────────┐
   │  🔄 Translating... Batch 12/25 (600/1,243 rows)     │
   │  ████████████░░░░░░░░░░░░  48%                      │
   │                                                     │
   │  Successful: 598  Failed: 2  Remaining: 643         │
   │                                                     │
   │  [Cancel Run]                                       │
   └─────────────────────────────────────────────────────┘

3. Translation phase completes, insert phase begins:
   ┌─────────────────────────────────────────────────────┐
   │  📤 Submitting to Superset...                       │
   │  Status: executing · Query #a7f3b2c                 │
   │                                                     │
   │  [View in Superset]                                 │
   └─────────────────────────────────────────────────────┘

4. On completion, system shows result:
   ┌─────────────────────────────────────────────────────┐
   │  ✅ Run Complete                                    │
   │                                                     │
   │  Translation: 1,241 rows (2 failed, batch 14)       │
   │  Insert:      ✅ Succeeded · 1,241 rows affected    │
   │  Superset:    Query #a7f3b2c · 2.3s                 │
   │                                                     │
   │  ── Generated SQL (audit) ──                        │
   │  INSERT INTO products_i18n (product_id,             │
   │    translated_name) VALUES                           │
   │    ('PRD-001', 'Беспроводная мышь'), ...            │
   │                                                     │
   │  [Retry Failed]  [View SQL]  [Retry Insert]         │
   └─────────────────────────────────────────────────────┘

5. User clicks [Retry Failed] → system re-processes only
   the 2 failed rows and re-submits to Superset.

6. If insert fails: [Retry Insert] re-submits the same SQL
   to Superset API without re-translating.
```

### Flow D: Translation History

```
1. User navigates to "Translation History".

2. System shows a filterable list:
   ┌──────────────────────────────────────────────────────────┐
   │  Translation History                                     │
   │                                                          │
   │  Filter: [Datasource ▼] [Target Table ▼] [Date range]   │
   │                                                          │
   │  ┌────────────────────────────────────────────────────┐  │
   │  │ Date       │ Datasource    │ Target Table  │ Rows  │  │
   │  ├────────────┼───────────────┼───────────────┼───────┤  │
   │  │ 2026-05-08 │ products_main │ products_i18n │ 1,241 │  │
   │  │ 14:32      │               │               │ ✅    │  │
   │  ├────────────┼───────────────┼───────────────┼───────┤  │
   │  │ 2026-05-07 │ categories    │ cat_names_ru  │ 89    │  │
   │  │ 09:15      │               │               │ ⚠️    │  │
   │  └────────────┴───────────────┴───────────────┴───────┘  │
   │                                                          │
   │  [Duplicate selected job]                                │
   └──────────────────────────────────────────────────────────┘

3. Clicking a run opens detail view with:
   - Configuration snapshot at time of run
   - Prompt template used
   - Source rows → translations (with edit marks)
   - Generated INSERT statements
    - Execution outcome and SQL Lab session reference (if applicable)
```

### Flow E: Terminology Dictionary Management

```
1. User navigates to "Dictionaries" section.

2. System shows list of existing dictionaries:
   ┌─────────────────────────────────────────────────────┐
   │  Terminology Dictionaries                           │
   │                                                     │
   │  [+ New Dictionary]                                 │
   │                                                     │
   │  ┌───────────────────────────────────────────────┐  │
   │  │ Name          │ Language │ Terms │ Attached to │  │
   │  ├───────────────┼──────────┼───────┼─────────────┤  │
   │  │ Product Terms │ ru       │ 142   │ 2 jobs      │  │
   │  │ Legal Glossary │ ru      │ 38    │ 1 job       │  │
   │  │ Finance Dict  │ ru      │ 67    │ —           │  │
   │  └───────────────────────────────────────────────┘  │
   └─────────────────────────────────────────────────────┘

3. User clicks "Product Terms" → opens editor:
   ┌─────────────────────────────────────────────────────┐
   │  Dictionary: Product Terms    Language: Russian      │
   │                                                     │
   │  [+ Add Term]  [Import CSV]  [Export]               │
   │                                                     │
   │  ┌──────────────────────────────────────────────┐   │
   │  │ # │ Source Term     │ Target Translation    │   │
   │  ├───┼─────────────────┼───────────────────────┤   │
   │  │ 1 │ invoice         │ накладная             │   │
   │  │ 2 │ widget          │ виджет                │   │
   │  │ 3 │ backorder       │ предзаказ             │   │
   │  │ 4 │ SKU             │ артикул               │   │
   │  │ ...                                          │   │
   │  └──────────────────────────────────────────────┘   │
   │                                                     │
   │  Used by: «Products RU Translation», «Catalog RU»   │
   └─────────────────────────────────────────────────────┘

4. User clicks [+ Add Term] → new empty row appears inline.
   Types "shipment" → "отгрузка", presses Enter → saved.

5. User clicks [Import CSV] → file picker opens.
   System previews detected pairs, flags line 7 as duplicate:
   "⚠️ 'invoice' already exists → 'накладная'. Import would
   add 'счёт'. [Keep existing] [Overwrite] [Skip]"

6. User resolves conflicts, confirms import → 45 new terms added.
```

### Flow F: Feedback Loop — Correct → Dictionary (Enhanced with Context)

```
1. User is viewing completed run results (Flow D detail view).

2. In the translations table (multi-language), user notices:
   ┌──────┬──────────────┬──────┬──────────────────┬──────────────────┐
   │  #   │ Source (fr)  │ Lang │   en             │   de             │
   ├──────┼──────────────┼──────┼──────────────────┼──────────────────┤
   │  4   │ "Voiture"    │  fr  │ "Car" [✏️] [📕D] │ "Auto"           │
   │      │ Cat: Parts   │      │                  │                  │
   └──────┴──────────────┴──────┴──────────────────┴──────────────────┘
   Notes: [✏️] = click to edit cell; [📕D] = submit to dictionary

3. User clicks [✏️] on "Car" → cell becomes inline input:
   ┌──────────────────────────────────────────┐
   │  [Vehicle___________________________] ✓  │
   └──────────────────────────────────────────┘
   User types "Vehicle", clicks ✓ → cell saves, shows 📝 badge.

   After save, [📕D] button appears → user clicks it.

4. Correction popup opens with context:
   ┌──────────────────────────────────────────────────────────┐
   │  ✏️ Submit to Dictionary                                 │
   │                                                          │
   │  Source term:      [Voiture                        ]    │
   │  Source language:  [French (fr)                    ▼]   │
   │  Target language:  [English (en)                   ▼]   │
   │  New translation:  [Vehicle                        ]    │
   │                                                          │
   │  ─── Context ─────────────────────────────────────────  │
   │  🤖 Auto-detected from source row:                      │
   │  ┌──────────────────────────────────────────────────┐   │
   │  │ Category: Automotive parts                        │   │
   │  │ Product type: Engine component                    │   │
   │  │ Description: Voiture d'essieu arrière             │   │
   │  └──────────────────────────────────────────────────┘   │
   │  [Edit] [Remove context]                                 │
   │                                                          │
   │  📝 Usage notes (optional):                              │
   │  ┌──────────────────────────────────────────────────┐   │
   │  │ Use "Vehicle" for entire vehicle assembly.       │   │
   │  │ "Car" only for passenger automobiles in casual   │   │
   │  │ contexts.                                        │   │
   │  └──────────────────────────────────────────────────┘   │
   │                                                          │
   │  ─── Prompt Preview ──────────────────────────────────  │
   │  "Voiture" (context: Category=Automotive parts,         │
   │   Product type=Engine component) → "Vehicle"            │
   │   # Usage: Use "Vehicle" for entire vehicle assembly    │
   │                                                          │
   │  Dictionary:       [Main Glossary                  ▼]   │
   │                                                          │
   │  [Cancel]  [Submit to Dictionary]                       │
   └──────────────────────────────────────────────────────────┘

5. User reviews, optionally edits context or adds notes,
   then clicks [Submit to Dictionary].

6. If "Voiture → Vehicle" (fr→en) already exists:
   ┌──────────────────────────────────────────────────┐
   │  ⚠ Term already exists                          │
   │                                                  │
   │  "Voiture" → "Vehicle" already exists in         │
   │  "Main Glossary" (fr → en).                      │
   │  Existing context: Category=Automotive           │
   │  New context: Category=Automotive parts,         │
   │              Product type=Engine component        │
   │                                                  │
   │  [Overwrite]  [Keep Existing]  [Cancel]          │
   └──────────────────────────────────────────────────┘

7. User chooses [Overwrite] → entry updated with new context.
   Badge appears: ✅ "Vehicle ✓" on the corrected cell.

8. For bulk corrections with context:
   User clicks [Bulk Replace...] → modal opens:
   ┌──────────────────────────────────────────────────────┐
   │  🔄 Bulk Find & Replace                              │
   │                                                      │
   │  Find:    [Car________________]  [🔍 Regex?]         │
   │  Replace: [Vehicle_____________]                     │
   │  Language: [en ▼]                                    │
   │                                                      │
   │  ☑ Submit to dictionary with context                  │
   │     Dictionary: [Main Glossary ▼]                    │
   │     Usage notes: [Applied to all entries______]       │
   │                                                      │
   │  [Preview]                                            │
   │  ┌──────┬──────────┬──────────┬──────────┬────────┐ │
   │  │  #   │ Source   │ Current  │ New      │ Context│ │
   │  ├──────┼──────────┼──────────┼──────────┼────────┤ │
   │  │  4   │ Voiture  │ Car      │ Vehicle  │ Parts  │ │
   │  │  12  │ Voiture  │ Car      │ Vehicle  │ Parts  │ │
   │  │  28  │ Voiture  │ Car      │ Vehicle  │ Parts  │ │
   │  └──────┴──────────┴──────────┴──────────┴────────┘ │
   │  ⚠ Same term across rows with different contexts —   │
   │  only first row's context will be saved.             │
   │                                                      │
   │  [Cancel]  [Apply to 3 rows]                         │
   └──────────────────────────────────────────────────────┘
```

### Flow G: Schedule Configuration

```
1. User opens a saved translation job and clicks "Schedule" tab.

2. System shows schedule configuration:
   ┌─────────────────────────────────────────────────────┐
   │  Schedule — Products RU Translation                 │
   │                                                     │
   │  Schedule type:  (●) Cron  ( ) Interval  ( ) Once  │
   │                                                     │
   │  Cron expression: [0 6 * * 1_______________]        │
   │  ↳ Every Monday at 06:00                            │
   │                                                     │
   │  ── Upcoming executions ──                          │
   │  Mon, 11 May 2026 06:00                             │
   │  Mon, 18 May 2026 06:00                             │
   │  Mon, 25 May 2026 06:00                             │
   │                                                     │
    │  Insert submission: via Superset API (always)      │
    │  Concurrency:        (●) Skip if previous running   │
   │                      ( ) Queue after previous       │
   │                                                     │
   │  Status: ● Active   [Disable]                       │
   │                                                     │
   │  Last run:  2026-05-04 06:00 — ✅ 1,231 rows        │
   │  Next run:  2026-05-11 06:00                        │
   └─────────────────────────────────────────────────────┘

3. User toggles to "Interval" type:
   ┌─────────────────────────────────────────────────────┐
   │  Schedule type:  ( ) Cron  (●) Interval  ( ) Once  │
   │                                                     │
   │  Every: [24] [▼ hours     ]                         │
   │  Starting at: [2026-05-09 02:00_______________]     │
   │                                                     │
   │  ── Upcoming executions ──                          │
   │  Sat, 09 May 2026 02:00                             │
   │  Sun, 10 May 2026 02:00                             │
   │  Mon, 11 May 2026 02:00                             │
   └─────────────────────────────────────────────────────┘

4. User clicks [Disable] → schedule pauses:
   "Schedule paused. No new runs will be triggered.
   [Re-enable] to resume."
   Next planned execution line is grayed out.

5. When user edits job config with active schedule:
   ┌──────────────────────────────────────────────────────┐
   │  ⚠ This job has an active schedule (Mon 06:00).     │
   │  Configuration changes will apply to the next        │
   │  scheduled run. Continue?                            │
   │                                                      │
   │  [Cancel]  [Save & Update Schedule]                  │
   └──────────────────────────────────────────────────────┘
```

## 4. The "Error" Experience

**Philosophy**: Don't just report the error; preserve progress and guide the user to targeted recovery.

### Scenario A: Datasource Unavailable

*   **User Action**: Opens a saved translation job whose datasource was deleted or renamed.
*   **System Response**:
    *   Banner at top: "⚠️ Datasource 'products_old' is no longer available. Select a replacement datasource to continue."
    *   Column mappings are cleared; key and target settings are preserved.
    *   [Select Replacement Datasource] button is prominent.
*   **Recovery**: User selects a new datasource, remaps columns, and the job is usable again.

### Scenario B: LLM Batch Failure

*   **System Response**:
    *   Progress bar pauses at the failed batch.
    *   Warning: "⚠️ Batch 14 failed: rate limit exceeded. 2 rows affected."
    *   Remaining batches continue processing.
    *   At completion: "1,241 rows translated, 2 failed. [Retry Failed Rows]"
*   **Recovery**: User clicks [Retry Failed Rows]. System re-processes only the 2 affected rows. If rate limits persist, system suggests increasing delay between batches or reducing batch size.

### Scenario C: Key Column NULL in Source Data

*   **System Response**:
    *   During run, rows with NULL key values are collected.
    *   At completion: "⚠️ 15 rows were skipped because key column 'region_code' contained NULL values."
    *   [Download skipped rows] link to download a CSV of unprocessable rows.
*   **Recovery**: User fixes NULL key values in the source and re-runs the job (existing successful rows are skipped by default).

### Scenario D: Target Table Not Found

*   **User Action**: Specifies a target table that does not exist in Superset.
*   **System Response**:
    *   At save time: "❌ Target table 'products_i18n' was not found in Superset. Please verify the table name or create it first."
    *   Save is blocked.
*   **Recovery**: User either corrects the table name or creates the table in Superset, then saves.

### Scenario E: Configuration Modified During Run

*   **User Action**: Edits job configuration while a run is in progress.
*   **System Response**:
    *   Banner: "ℹ️ A translation run is currently in progress. It will continue using its existing configuration snapshot. Your changes will apply to future runs only."
    *   Save is allowed; no run cancellation.
*   **Recovery**: User saves changes. In-progress run completes unaffected. Next manual or scheduled run uses updated configuration.

### Scenario F: Dictionary Deletion Blocked by Active Job

*   **User Action**: Attempts to delete a dictionary that is attached to an active or scheduled translation job.
*   **System Response**:
    *   Dialog: "❌ Cannot delete 'Product Terms'. It is attached to 2 active jobs: 'Products RU Translation' (scheduled), 'Catalog RU'. Detach the dictionary from these jobs first."
    *   [Show attached jobs] link opens a filtered view.
*   **Recovery**: User opens each job, removes the dictionary attachment, then returns to delete. Or detaches via a bulk action from the dialog.

### Scenario G: Dictionary Import with Malformed File

*   **User Action**: Imports a CSV that has wrong column count, encoding issues, or mixed delimiters.
*   **System Response**:
    *   Preview shows: "⚠️ 12 rows could not be parsed. Row 5: only 1 column detected (expected 2). Row 8: encoding error."
    *   Parsable rows are shown; unparsable rows are listed with line numbers and reasons.
*   **Recovery**: User can download the unparsable rows as a separate file for manual correction, or adjust the import settings (delimiter, encoding) and retry.

### Scenario H: Scheduled Run — No New Source Rows

*   **System Response**:
    *   Before processing, system detects that all key-column values in the source already exist in the previous successful run (new-key-only strategy).
    *   Banner: "ℹ️ No new rows detected since last run (2026-05-04). All 1,231 keys already translated. Run skipped."
    *   A «no-new-rows» run record is created for audit but no INSERT statements are generated.
*   **Recovery**: User can trigger a manual run from the job page if full retranslation is needed (e.g., prompt or dictionary changed).

### Scenario I: Scheduled Run Failure Notification

*   **User Action**: Scheduled run fails (LLM quota exhausted at 06:00 on Monday).
*   **System Response**:
    *   Run record marked as ❌ failed with error details.
    *   Notification sent (if configured): "Scheduled translation 'Products RU' failed: LLM quota exceeded. Next attempt: Mon, 18 May 06:00. [View Details]"
    *   Schedule remains active for the next trigger.
*   **Recovery**: User recharges LLM quota, opens the failed run, and clicks [Retry Failed Run] to reprocess immediately without waiting for the next schedule.

## 5. Tone & Voice

*   **Style**: Concise, technical but approachable. Use short sentences with clear action verbs.
*   **Terminology**: 
    *   "Datasource" (not "data source" or "dataset") — aligned with Superset terminology.
    *   "Translation column" — the column whose values are translated.
    *   "Context columns" — columns that provide semantic context to the LLM.
    *   "Key columns" — columns that uniquely identify rows for target-table INSERT matching.
    *   "Materialized table" — the target table that physically stores translated data.
    *   "INSERT statements" — the generated SQL output, not "queries" or "scripts".
    *   "Batch" — a group of rows processed in one LLM API call.
    *   "Run" — a single execution of a translation job.
    *   "Terminology dictionary" / "Dictionary" — a named, language-specific collection of source_term → target_translation pairs.
    *   "Dictionary entry" — a single term pair inside a dictionary.
    *   "Feedback loop" — the workflow of correcting a translation in run results and submitting the correction to a dictionary.
    *   "Schedule" — a recurring trigger configuration (cron, interval, or one-time) attached to a translation job.

## 6. Frontend Integration Notes

*   **Routes**: 
    *   `/translate` — Translation Jobs list and management.
    *   `/translate/dictionaries` — Terminology dictionary management.
    *   `/translate/history` — Translation run history (may be a sub-view of jobs).
*   **Components**:
    *   `TranslationJobList` — list of saved translation jobs with status and schedule indicators.
    *   `TranslationJobConfig` — the configuration form (Flow A), with dictionary attachment and schedule tab.
    *   `TranslationPreview` — the side-by-side preview view (Flow B), now with per-language columns and configurable sample size.
    *   `TranslationRunProgress` — live progress during execution (Flow C), now with per-language progress bars.
    *   `TranslationRunResult` — completion summary with generated SQL and inline feedback-loop controls (Flow C + Flow F), now with click-to-edit cells and per-language statistics.
    *   `TranslationHistory` — filterable history of past runs (Flow D), now with per-language columns.
    *   `DictionaryList` — list of dictionaries with term counts and info (Flow E).
    *   `DictionaryEditor` — inline term editor with import/export, context display, language pair columns (Flow E).
    *   `TermCorrectionPopup` — the feedback-loop popup for submitting corrected terms with context capture, usage notes, and prompt preview (Flow F).
    *   `CorrectionCell` — inline-editable cell component used inside TranslationRunResult (Flow F).
    *   `BulkReplaceModal` — modal for bulk find-and-replace with context-aware dictionary submission (Flow F).
    *   `ScheduleConfig` — schedule type selector, cron/interval inputs, upcoming preview, auto-INSERT and concurrency settings (Flow G).
    *   `BulkCorrectionSidebar` — sidebar for selecting and correcting multiple terms at once (Flow F).
*   **State Management**: Job configuration, preview state, run progress, dictionary data, and schedule state are managed through Svelte stores with API-backed persistence.
*   **API Surface**: Standard REST endpoints under `/api/translate/` for CRUD on jobs, dictionaries, preview triggering, run execution, schedule management, feedback-loop submission, and history retrieval.
*   **WebSocket**: Real-time progress updates during batch translation via existing WebSocket infrastructure (consistent with Task Drawer patterns). Schedule trigger events are logged server-side without WebSocket push (user sees results on next page load or via notification).
*   **Contract Mapping**:
    *   `@UX_STATE`: Enumerate states per component — idle, loading, configured, previewing, running, completed, failed, retrying, scheduled, schedule_paused, dictionary_attached, dictionary_empty, import_preview, import_conflict, correction_view, correction_editing, correction_saving, correction_submitting_dict, correction_dict_submitted, correction_conflict, context_auto, context_editing, context_removed, context_with_notes, bulk_replace_configuring, bulk_replace_previewing, bulk_replace_applying.
    *   `@UX_FEEDBACK`: Loading skeletons during datasource fetch; spinners during LLM calls; toast notifications for save/delete; progress bar during run; syntax-highlighted SQL output; badge for dictionary-attached count; «Added to Dictionary» confirmation badge on corrected rows; «Dictionary ✓» badge on saved entries; schedule status indicator (active/paused); upcoming execution timeline; context-source badge (🤖 Auto/✏️ Edited/📝 Manual); prompt preview card in correction popup.
    *   `@UX_RECOVERY`: Retry button for failed batches; datasource replacement flow; NULL-key download; configuration duplication from history; dictionary conflict resolution dialog; import error row download; schedule force-run override; failed-schedule-run retry; correction save retry; context edit revert.
    *   `@UX_REACTIVITY`: Column list derived from selected datasource (`$derived`); batch progress computed from completed/total counts; cost estimate reactivity on batch size change; next N execution times derived from schedule config; term count reactively updated on add/delete; dictionary attachment list reactivity; correction context preview `$derived` from context_data + usage_notes; bulk replace affected rows `$derived` from pattern + run data.
