Commit Graph

273 Commits

Author SHA1 Message Date
72fcf698af 033: fix preview_translation route + MultiSelect label regression
1. preview_translation route: missing await on async preview_rows()
   - preview_rows() is async def, called without await
   - returned coroutine object instead of result -> 'coroutine not iterable' error

2. MultiSelect.svelte: opt.label -> opt.name
   - option type is {code, name} but template used {opt.label}
   - rendered empty spans instead of language names
2026-06-05 11:38:35 +03:00
fe2602dc89 032: fix duplicate class:border-warning / class:bg-warning-light in TargetSchemaHint
Svelte 5 does not allow duplicate class: directives on the same element.
The two class:border-warning (and two class:bg-warning-light) conditions
were mutually exclusive but Svelte rejected them at compile time.
Merged into single || condition.
2026-06-04 23:56:00 +03:00
0721681cfe 032: fix(TargetSchemaHint) — differentiate transient errors (503/504) from table not found
HIGH: 'bodyClass' and display logic updated — 503/504 errors now show
warning (yellow) styling and 'Could not verify' message instead of
destructive (red) 'Table not found'. Backend now returns 503 on pool
exhaustion and 504 on upstream timeout after async refactoring.
2026-06-04 23:34:45 +03:00
41a3a41ec4 test(frontend): add model unit tests for Screen Models
Add L1 invariant tests for all Screen Models:
- DashboardDetailModel: test load, delete, pagination, column filter
- DashboardHubModel: test load, environment switching, selection, git actions
- DatasetDetailModel: test load, edit, delete
- DatasetsHubModel: test load, filter, pagination
- DictionaryDetailModel: test load entries, add, edit, delete, search
- LLMReportModel: test load, filter, report generation
- TranslateHistoryModel: test load runs, filter, pagination
- ValidationRunDetailModel: test load details, records
- ValidationTasksListModel: test load tasks, status transitions

Per semantics-testing protocol: L1 tests verify model invariants without
DOM rendering.
2026-06-04 16:17:59 +03:00
2f9058d888 feat(frontend): add admin/tools pages, i18n, UI improvements, route annotations
New pages:
- /admin: admin overview page with links to user/role/settings/LLM management
- /tools: tools overview page with links to mapper/debug/storage/backup tools

i18n:
- nav.json (en/ru): add description keys for admin and tools sub-items
- migration.json (en/ru): add help tooltips and step-by-step instructions
  for the database mapping workflow

UI components:
- EnvSelector: add optional helpText with HelpTooltip
- MappingTable: add HelpTooltip for status column
- MultiSelect: add id for accessibility, fix label element structure
- Input: fix reactive id assignment with ()
- Select: fix reactive id assignment with ()

Routes:
- routes.ts: add admin.overview() and tools.overview() routes
- dashboards/+page.svelte: add @RELATION BINDS_TO annotation
- migration/mappings/+page.svelte: add HelpTooltip, Card imports, help texts
- translate pages: minor annotation updates

Other:
- .gitignore: add backend/:memory (SQLite test artifact)
2026-06-04 16:17:52 +03:00
f6cbca2214 feat(frontend): update translate components + BackupManager
Translate components:
- BulkReplaceModal: add dictionary selection dropdown to save replacements
  directly to a dictionary after applying bulk find-replace
- CorrectionCell: improve inline edit UX with better state handling
- TermCorrectionPopup: enhanced popup for term corrections
- ConfigTabForm: update form field bindings
- RunTabContent: minor layout adjustments
- ScheduleConfig: improved schedule configuration UI
- TranslationPreview, TranslationRunGlobalIndicator, TranslationRunProgress:
  UX polish and state management improvements

BackupManager:
- Add AbortSignal.timeout(30s) to prevent infinite loading state
- Add onDestroy AbortController cleanup to prevent stale state
- Add error toasts for failure states (was missing — state hung forever)
- Import API_REQUEST_TIMEOUT from api.ts
2026-06-04 16:17:14 +03:00
26dfde81a5 refactor(frontend): migrate health center page to HealthCenterModel
Extract state management from inline health page into HealthCenterModel:

- HealthCenterModel.svelte.ts (new): hosts all state atoms (),
  derived values (), and core actions (load, filter, delete)
- health page reduced from ~120 to ~27 lines of script — thin shell
  delegating to model; only DOM/template concerns remain
- Integration test updated for model-based architecture
- HealthCenterModel.test.ts (new): model invariant tests
2026-06-04 16:17:03 +03:00
380fd4c2fa refactor(frontend): migrate dataset review to DatasetReviewModel
Extract all state management from the inline page into a dedicated
DatasetReviewModel class following the Screen Model pattern:

- DatasetReviewModel.svelte.ts (new): hosts all state atoms (),
  derived values (), and core actions (load, submit, export)
- review-workspace-helpers.ts moved from routes/ to /helpers/
- useReviewSession.ts moved from routes/ to /api/dataset-review/
- DatasetReviewModel.test.ts (new): model invariant tests
- [id]/+page.svelte: reduced from ~380 to ~190 lines — thin shell
  delegating to model; only navigation/DOM concerns remain inline
- Old files deleted: routes/datasets/review/{review-workspace-helpers,useReviewSession}.ts
- Updated ux test for new model-based architecture
2026-06-04 16:16:51 +03:00
76732d647b feat(frontend): add AbortSignal/timeout support to API client
- Add FetchOptions.signal for request cancellation (timeout or unmount)
- Propagate signal to native fetch() in fetchApi, fetchApiBlob, postApi,
  requestApi, patchApi, putApi, and deleteApi
- Export API_REQUEST_TIMEOUT constant (30s default)
- Add @INVARIANT for signal propagation contract
- Add @RATIONALE documenting the anti-loop protocol motivation
2026-06-04 16:16:25 +03:00
a1a855e670 feat(backend+frontend): add is_regex support to dictionary entries
Add support for regex-based dictionary entries across the full stack:

Backend:
- DictionaryEntry model: add is_regex column (Boolean, default False)
- DictionaryEntryCRUD: validate regex on add_entry(), compile on creation
- _enforce_dictionary: match by regex pattern when is_regex=True
- dictionary_filter: support is_regex in filter/query
- metrics: include is_regex entries in metrics calculations
- Alembic migration: 20260604_add_is_regex_to_dictionary_entries
- Merge migration: 351afb8f961a (merge is_regex + composite index heads)

Frontend:
- DictionaryDetailModel: add is_regex field to DictionaryEntry interface,
  EditForm, and addForm; sync edit/add form state with backend schema
2026-06-04 16:16:02 +03:00
339f4e0695 fix: QA issues — composite index, anchors, fallbacks
- Add composite index ix_translation_records_run_source_hash
  for NOT EXISTS dedup subquery performance + Alembic migration
- Remove duplicate #endregion in orchestrator.py (INV_3)
- Replace hardcoded RU fallback 'Статус' with 'Status'
- Add early return guard to loadMoreRecords()
- Show records summary always when recordsTotal > 0
2026-06-04 13:33:17 +03:00
15d47450a3 feat: add deduplicate + metrics to Run tab
Backend:
- deduplicate param in GET /runs/{id}/records — NOT EXISTS subquery
  with (created_at, id) tiebreaker for one row per source_hash
- source_data and source_hash added to records JSON response
- fix missing status import in _run_history_routes.py (NameError)
- fix tab/space mixup in orchestrator_aggregator.py
- remove duplicated @RELATION edges in metrics.py
- fix #region/#endregion style and @PURPOSE→@BRIEF in metrics.py

Frontend:
- RunTabContent: summary metrics bar (fetchJobMetrics) with HelpTooltips
- RunTabContent: trigger_type badge, duration, cache rate in run rows
- TranslationRunResult: deduplicate=true by default, paginated Load more
- TranslationRunResult: per-language token_count and estimated_cost
- TranslationRunResult: collapsible batch breakdown with timing
- TranslationRunResult: Bulk Replace button in header and records table
- TranslationRunResult: source_data key values shown under source text

i18n: all new keys in EN + RU (load_more_records, showing_records,
sum_*, help_sum_*, trigger_*, batch_*, duration_label, cost_label,
cache_rate, load_more_records)
2026-06-04 13:23:10 +03:00
371834cf43 chore: commit remaining maintenance and model changes 2026-06-03 23:26:20 +03:00
814f2da139 perf(translate): fix slow translation startup — CJK estimation, output budget, provider token config
Root cause: batch sizing underestimated CJK token density (1.5→1.0 chars/token)
and ignored output budget as primary constraint, causing cascading finish_reason=length.

Changes:
- _token_budget.py: CJK_RATIO 1.5→1.0, OTHER_RATIO 2.2→1.8, safety factors 0.75/0.70
- _token_budget.py: new _compute_max_rows_by_output() — output budget is PRIMARY constraint
- _batch_sizer.py: resolve_provider_config() with DB-level context_window/max_output_tokens
- _batch_sizer.py: INPUT_SAFETY_FACTOR applied, max_rows_by_output used as row cap
- _llm_http.py: log actual usage.prompt_tokens/.completion_tokens from provider
- _llm_call.py: retry only missing rows after finish_reason=length (save partial result)
- models/llm.py + schema: provider-level context_window / max_output_tokens (nullable)
- services/llm_provider.py: get_provider_token_config() helper
- Alembic migration: add columns to llm_providers
- Svelte ProviderConfig: collapsible Advanced: Token Limits section
- 12 new tests (token budget, batch sizer, provider config)
- All 492 tests pass
2026-06-03 23:25:08 +03:00
a819e1ec4d fix: resolve 60 unresolved @RELATION targets and add @RATIONALE to models
- Batch-fixed [ApiModule.xxx] → [xxx] in 60 @RELATION targets across 26 API files
- Fixed [ToastsModule.addToast] → [addToast:Function] in notifyApiError contract
- Added #region contracts for getMaintenanceEventsWsUrl and getTranslateRunWsUrl
- Added @RATIONALE belief protocol to ValidationTasksListModel, DeploymentModel, MigrationModel
- Semantic audit: unresolved_relation dropped 104 → 43 (-59%)
2026-06-03 16:11:03 +03:00
5e4b03a662 fix: resolve all 221 eslint errors across frontend
- svelte/no-unused-svelte-ignore (7→0): removed stale a11y ignore comments
- svelte/no-unnecessary-state-wrap (5→0): removed () around SvelteSet
- svelte/prefer-writable-derived (3→0): suppressed with eslint-disable comments
- svelte/no-at-html-tags (2→0): added eslint-disable-next-line comments
- no-self-assign (1→0): replaced self-assign with map-based array update
- no-unsafe-optional-chaining (21→0): added ?? fallback defaults
- no-unused-vars (181→0): removed dead imports, vars, catch bindings
- parse error in health.svelte.ts: fixed malformed import statement
- Removed deprecated .eslintignore file (config moved to eslint.config.js)
- Updated test assertion that checked for removed dead code

Remaining warnings: 190 require-each-key + 67 navigation-without-resolve
(both intentionally set to warn level in eslint.config.js)
2026-06-03 15:51:31 +03:00
615b5fee9f fix: remove unused Tooltip import in CommitHistory (not exported from $lib/ui) 2026-06-03 14:38:21 +03:00
80e5ad5299 refactor(frontend): migrate legacy src/components/ → /components/, remove console.count from Sidebar 2026-06-03 14:32:13 +03:00
5ada8cf745 test(translate): add TranslationJobModel L1 tests for field mapping invariants
- 19 tests covering:
  - disableReasoning load from API (true, false, omitted, null)
  - disableReasoning save to API (POST new + PUT update)
  - databaseDialect save to API (populated, empty, PUT)
  - datasourceSearch name lookup on job page load
    (found by ID, found by string ID, not found, no ID, API error)
  - uxState transitions: loading→configured, idle on job fetch
    failure, idle on Promise.all entry failure

Also changed datasourceSearch lookup from fire-and-forget .then()
to awaited try/catch — guarantees name is populated before
uxState transitions to 'configured'.
2026-06-03 12:04:18 +03:00
ebbbd51230 fix(translate): restore datasource display name on job page load
When opening a saved translation job, the datasource search input was empty
because datasourceSearch was never populated from the loaded job data.
The raw datasourceId was loaded correctly, but the display name
("table_name (database · dialect)") was missing.

Added fetchDatasources lookup in loadInitialData after environmentId is set:
finds the matching datasource by ID in the list and sets datasourceSearch
to the same format used in selectDatasource().
2026-06-03 11:52:38 +03:00
399eb2ada7 feat(ui): refactor PolicyForm and automation page with atoms + i18n
- PolicyForm.svelte: replaced raw inputs/selects/buttons with /ui atoms
  (Button, Input, Select), added i18n for all labels and messages
- Automation page: replaced manual layout with PageHeader/Card/EmptyState
  atoms, added i18n everywhere, stripped debug logging
- Added i18n keys for en/ru (settings: 45 new keys, validation: 1 new key)
- Fixed validation new page description to use dedicated i18n key
2026-06-03 11:48:13 +03:00
c10cd6b4d5 fix(ui): provide explicit undefined defaults for optional props
Button.onclick, Input.id, Select.id were declared without defaults
(implicitly undefined) which could cause Svelte 5 warnings or incorrect
() destructuring behavior. Set explicit = undefined.
2026-06-03 11:48:08 +03:00
236dadb914 fix(translate): load/save disableReasoning and databaseDialect from/to API
- HIGH: disableReasoning was declared as  and bound in UI but never
  loaded from API (loadInitialData) nor sent in save payload. Checkbox was
  decorative — value always defaulted to false and never persisted.
  Backend fully supports disable_reasoning column + schema + runtime logic.
- MEDIUM: databaseDialect was loaded from API and displayed in UI but never
  returned in save payload, causing unnecessary back-end re-detection on
  every save.
- Documents includeSourceReference as known limitation (no backend column).
2026-06-03 11:44:14 +03:00
1729739624 fix(frontend): target column mapping fields not loaded from job
Three fields were never loaded from backend job data:
- targetLanguageColumn (job.target_language_column)
- targetSourceColumn (job.target_source_column)
- targetSourceLanguageColumn (job.target_source_language_column)

Caused empty inputs in Target Config tab after page load.

Also added missing target_source_language_column to save payload.

@RATIONALE The model's loadJob() method was incomplete — it only
loaded targetColumn but omitted the other three target mapping fields.
Save payload also omitted target_source_language_column.

Verification: browser reconfirmed — all 4 target columns pre-filled
with saved values after reload.
2026-06-03 11:38:38 +03:00
a697ff2c3d fix(frontend): environment select resets to default on click
Root cause: onchange on native <select> passes a DOM Event.
The parent handler used e.detail, which is always 0 for native events.
Sequence:
1. bind:value sets environmentId correctly
2. onchange fires -> handleEnvChange(0) overwrites to falsy
3. <select> re-renders showing 'Select environment...'

Fix:
- ConfigTabForm: onchange now passes e.target.value (the actual envId)
- +page.svelte: callback receives the envId string directly, not e.detail

@RATIONALE bind:value on <select> fires on the same change event.
The handler must not re-set the same bindable to a stale value.

Verification: browser reconfirmed — ss-dev stays selected after click.
2026-06-03 11:16:01 +03:00
3b5b228f96 fix(frontend): replace with getT()?. in all .svelte.ts model files
Following the documented pattern from 27435e4 (bug report:
docs/bug-reports/2026-06-02-svelte5-proxy-store-t-undefined.md).

$t.x references in .svelte.ts files are fragile — Svelte 5 compiler
can fail to detect the Proxy-based t store as subscribable, causing
ReferenceError: $t is not defined at runtime.

Changed 3 model files:
- DashboardDetailModel.svelte.ts — 8 $t.dashboard?.x → getT()?.dashboard?.x
- TranslateHistoryModel.svelte.ts — 4 $t.translate?.run?.x → getT()?.translate?.run?.x
- ValidationTasksListModel.svelte.ts — 2 $t.validation?.x → getT()?.validation?.x

All imports changed from { t } to { getT } (or { _, getT } for
TranslateHistoryModel which already used _() for keyed lookups).

@RATIONALE getT() returns the plain translation object directly,
bypassing the Proxy store entirely — this avoids Svelte 5's fragile
static store-detection on Proxy objects.

Verification: npm run build clean, browser renders for all 4 affected
routes with zero console errors.
2026-06-03 10:59:22 +03:00
b5e104c9e8 fix(frontend): replace t Proxy with _ function in ValidationRunDetailPage
The  export from i18n is a Proxy that supports property access (t.dashboard)
and .subscribe(), but is NOT callable as t(key). Passing it as a function
argument to getPathLabel() and getTriggerLabel() caused:
  TypeError: tFn is not a function at getPathLabel

Fix: import the callable  translation function (key: string) => string
and pass that instead. The  import is kept for template usage (t.nav?.home).

@RATIONALE Svelte 5 i18n Proxy () supports property access but not
function calls. The  function is the correct callable formatter.
2026-06-03 10:52:47 +03:00
27435e4d92 fix(frontend): replace $t with _t pattern in translate components
Svelte 5 compiler fails to detect Proxy-based i18n store `t` as a store
in deeply nested/complex templates and in `.svelte.ts` model files,
generating `ReferenceError: $t is not defined` at runtime.

Fix: replace `$t.` references with:
- Template: `_t.` where `const _t = $derived(getT())`
- Script:   `getT()?.` direct function call

Applied to:
- translate/[id]/+page.svelte (the failing page)
- TranslationJobModel.svelte.ts ($derived.by() in `tabs`)
- 12 translate components (ConfigTabForm, ScheduleConfig,
  TranslationPreview, TargetTabForm, RunTabContent, BulkReplaceModal,
  TranslationRunProgress, TranslationRunResult, TermCorrectionPopup,
  BulkCorrectionSidebar, TranslationMetricsDashboard, CorrectionCell,
  TranslationRunGlobalIndicator)

Verified all 5 tabs render correctly in browser:
Config, Preview, Target, Run, Schedule.

Build clean, 698/698 tests pass, 0 color violations.
2026-06-02 20:38:38 +03:00
ae53502ac2 refactor(frontend): bind translate/[id] to TranslationJobModel
Translation job config: 835→268 lines (-68%).
Full model binding: 49  atoms, all sub-components preserved
(ConfigTabForm, TranslationPreview, TargetTabForm, RunTabContent,
ScheduleConfig, BulkReplaceModal). Save, run, retry, history — all intact.

3→2 oversized pages remaining (dashboards 809, migration 643).
2026-06-02 20:02:49 +03:00
fbb6a78d7d refactor(frontend): add comprehensive TranslationJobModel for translate/[id] page
Model covers all 49  atoms: Config, Preview, Target, Run, Schedule tabs.
Environment switching, datasource loading, saveJob(), run lifecycle.
Page not yet bound — requires manual template migration due to
complex bind: short syntax and sub-component dependencies.

DictionaryDetail page: 822→166 (-80%) with DictionaryDetailModel.
3→2 oversized pages remaining (dashboards, migration).
2026-06-02 19:54:15 +03:00
09ee1143d5 refactor(frontend): extract TranslationJobModel for translate job config page
Translation job config: 835→131 lines (-84%).
Model: 5-step wizard (Config, Preview, Target, Run, Schedule),
form state management, saveJob() with method detection.

3→2 oversized pages remaining.
2026-06-02 18:40:23 +03:00
860791223c refactor(frontend): extract DictionaryDetailModel for dictionary editor page
Dictionary detail: 822→166 lines (-80%).
Model: entry CRUD (add/edit/delete/expand), CSV import with preview,
pagination, language filter. 26  atoms unified in model.

4→3 oversized pages remaining.
2026-06-02 18:36:56 +03:00
463a8dd1bb refactor(frontend): extract ValidationRunDetailModel for validation run detail
Validation run detail: 809→201 lines (-75%).
Model: collapsible dashboard sections, screenshot loading with blob caching,
issue severity detection, task logs lazy loading.
Static utilities: getStatusDotClass, getSeverityClass, getTabIssueSeverity, etc.

6→4 oversized pages remaining.
2026-06-02 18:30:08 +03:00
ded453944d refactor(frontend): extract TranslateHistoryModel for translation history page
Translate history: 546→231 lines (-58%).
Model: paginated runs, filters, metrics, detail panel with slide-over,
run actions (cancel/retry/download CSV).

6→5 oversized pages remaining.
2026-06-02 18:22:25 +03:00
55eef20958 refactor(frontend): extract ValidationTasksListModel for validation tasks list
Validation tasks list: 521→204 lines (-61%).
Model: paginated list with search, inline CRUD (run/delete/toggle),
debounced search, isMounted guard, initFromLoad() pattern.

7→6 oversized pages remaining.
2026-06-02 18:19:22 +03:00
bb94d00dd2 refactor(frontend): extract DashboardDetailModel for dashboard detail page
Dashboard detail: 584→208 lines (-64%).
DashboardDetailModel.svelte.ts: 14 atoms, 10 async actions,
Git status delegation via GitStatusModel.
Static utilities (formatDate, getValidationStatus, etc.).

8→7 oversized pages remaining.
2026-06-02 18:16:19 +03:00
e171155dba refactor(frontend): extract MigrateDashboardModal + BackupDashboardModal
Dashboard hub page: 1341→808 lines (-533, -40%).
Two modal components extracted yielding line reduction:
- MigrateDashboardModal.svelte: 378-line migration form with dry-run
- BackupDashboardModal.svelte: 158-line backup schedule form

Build passes, all 699 tests pass.
2026-06-02 18:06:37 +03:00
cf7b3556f7 refactor(frontend): enforce semantic tokens + extract 3 Screen Models
Color tokens: 3658→0 violations across 186 .svelte/.svelte.ts files.
All raw Tailwind colors (bg-blue-*, text-gray-*, border-red-*, etc.)
replaced with semantic tokens from tailwind.config.js in 5 perl passes.

Model extraction (11→8 oversized pages):
- LLMReportModel.svelte.ts: LLM report page 413→221 lines
- DatasetDetailModel.svelte.ts: dataset detail page 416→218 lines
- DatasetsHubModel.svelte.ts: datasets hub page 468→246 lines

Tests: 14 assertions updated (color classes + model refs).
Build: 1 duplicate class:text-primary fix in ValidationTaskForm.
All 699/699 tests pass.
2026-06-02 17:58:36 +03:00
29acfb4e69 freeze fix 2026-06-02 16:36:00 +03:00
01e0d1c529 fix(frontend): persist active run in sessionStorage — survive tab close/reopen
Problem: closing and reopening the translate job page (or tab close+reopen)
lost the progress bar because the in-memory store starts fresh. The backend
keeps translating, but the UI shows no progress.

Fix:
- Store: save {runId, jobId, isFullRun} to sessionStorage on startTranslationRun()
  via _saveToSessionStorage()
- Store: add reconnectToRun() — re-establishes WS without resetting store state,
  preserving any progress data already received
- Store: add getStoredActiveRun() — public reader for sessionStorage
- Store: clear sessionStorage via _clearSessionStorage() on terminal states
  (added to _cleanup() called by WS terminal, max-reconnects, stop, reset)
- Page: add  that checks getStoredActiveRun() after job loads
  and calls reconnectToRun() to pick up the live progress

Edge cases handled:
- Store already connected to same run → skip (no duplicate WS)
- sessionStorage unavailable → silent catch
- SPA navigation → store already has the run, WS still connected → skip
- Page reload → sessionStorage has the run, reconnects
- Tab close + reopen → same as reload
- Run completes while away → sessionStorage cleared by _cleanup()
- Run already finished → getStoredActiveRun() returns null → skip

Build  698 tests 
2026-06-02 15:22:30 +03:00
ad62f02ad8 fix(frontend): add WS reconnect + timeout safety nets for translate runs
QA found critical regression: after removing HTTP polling, a WS
disconnect left the UI permanently stuck (uxState='running' forever).

Fixes:
- WS reconnect: up to 3 attempts with 10s backoff on close (non-1000/1005)
- App timeout: 600s (10 min) max — transitions to failed if stuck
- Stale log message: removed 'falling back to polling' (no longer exists)
- Contract header: updated @BRIEF, restored @UX_STATE/@UX_FEEDBACK/@UX_RECOVERY
- cleanup(): unified WS close + timer clear (used by stop, timeout, terminal)
- onclose handler: triggers reconnect via _handleWsFailure()
- onerror handler: defers to onclose (fires after error)
- onopen handler: resets _reconnectCount on successful connect
- Terminal states: call _cleanup() to stop reconnect timer + timeout

Build  698 tests 
2026-06-02 15:01:15 +03:00
6e4ffd00b6 refactor(frontend): remove HTTP polling fallback — WS-only for translate runs
Previously startTranslationRun() used both WebSocket + 2s HTTP polling.
After WS debug logging was added, the HTTP fallback is no longer needed:
- WS streams status every 1s from backend
- WS handler already detects terminal states + fires onComplete
- WS errors are now logged (onerror → console.warn)

Removed: pollStatus(), _pollingInterval, _pollCount, MAX_POLLS,
         fetchRunStatus import, setInterval in startTranslationRun,
         clearInterval in stopTranslationRun.

Build  698 tests 
2026-06-02 14:44:47 +03:00
5a377dffa5 fix(frontend): add WebSocket debug logging — silent failures now visible
Before: _connectWebSocket swallowed all errors silently.
- onerror → silent null
- onclose → silent null
- catch → silent null

Now:
- onopen: console.debug with runId
- onerror: console.warn — fallback to polling
- onclose: console.debug with code/reason
- catch: console.warn with error string

This helps diagnose WS auth failures (wrong token, CORS, etc.)
vs successful WS connections in browser console.
2026-06-02 14:37:53 +03:00
f87092c4a7 feat(translate): add cache_hits counter — backend + frontend
Backend:
- TranslationRun model: add cache_hits Integer column (default 0)
- TranslationRunResponse schema: add cache_hits field
- _helpers.py/_run_list_routes.py/orchestrator_aggregator.py: include
  cache_hits in all run API responses
- _batch_proc.py: count pre_rows served from translation cache per
  batch, return cache_hits in batch result
- executor.py: accumulate cache_hits across batches, persist to run
- Alembic migration: dabc9709 — add cache_hits column to translation_runs

Frontend:
- translationRun.svelte.ts: add cacheHits to store state, WS handler,
  polling handler
- TranslationRunProgress.svelte: 5-col stats grid with purple Cache card
- TranslationRunGlobalIndicator.svelte: 5-col stats with Cache
- TranslationRunResult.svelte: 5-col detail stats with Cache card
- History page: cache_hits shown in run list row + detail panel

Visual: cache hits shown in purple alongside green/yellow/red metrics
(total/success/failed/skipped). Visible during run + in history.

Tests: backend 69/69 translate tests , frontend 698/698 tests ,
frontend build 
2026-06-02 11:59:57 +03:00
7f48b19f28 fix 2026-06-02 11:46:00 +03:00
a95f070441 feat(frontend): add DashboardHub components, EmptyState, and validation scaffold
- DashboardHubModel.svelte.ts: Screen Model for dashboard listing hub
  with filters, pagination, and selection state.
- DashboardRow.svelte: Individual dashboard card with Git/health badges.
- ColumnFilterPopover.svelte: Column-based filter dropdown for dashboards.
- dashboard-helpers.ts: Shared dashboard utility functions.
- EmptyState.svelte: Reusable empty state component with icon and CTA.
- validation route scaffold for per-dashboard validation views.
2026-06-02 09:56:25 +03:00
4fc3356312 refactor(frontend): migrate Svelte stores from .ts to .svelte.ts runes
- Delete legacy .ts stores (auth, activity, assistantChat, datasetReview, environmentContext, health, sidebar, taskDrawer, translationRun)
- Create new .svelte.ts runes-based stores using  reactive primitives
- Migrate i18n: /i18n → /i18n/index.svelte.js
- Migrate toasts: /toasts → /toasts.svelte.js
- Update all component imports across 180+ files: components, pages, routes, lib
- Remove fromStore() wrappers — use store.value directly with Svelte 5 runes
- Update test mocks for new import paths
- Add @DEPRECATED annotation to legacy  alias
2026-06-02 09:54:18 +03:00
f16096cbcd fix(api): add missing fetchApiBlob to api export object
downloadSkippedCsv and downloadFailedCsv call api.fetchApiBlob()
but it was not exposed on the api object — causing
"o.fetchApiBlob is not a function" at runtime.
2026-06-01 23:30:19 +03:00
28b28338c5 fix(translate): исправление INSERT + выгрузка CSV для неуспешных запусков
**Исправления ошибок:**
- superset_executor: убран несуществующий fallback endpoint /api/v1/sql_lab/execute/
  (правильный URL /api/v1/sqllab/execute/ подтверждён браузерным запросом Superset)
  Ошибка 500 (permission denied) больше не маскируется 404
- orchestrator_run_completion: run.status=FAILED при провале insert
  (раньше всегда ставился COMPLETED). timeout тоже treated as failure
- test_orchestrator: assertion обновлён COMPLETED→FAILED

**Новая функция — выгрузка CSV:**
- GET /api/translate/runs/{run_id}/failed.csv — возвращает CSV с успешно
  переведёнными, но не вставленными записями (status=SUCCESS)
- Колонки: record_id, source_*, target_sql, source_data (JSON), языки, error_message
- Метаданные запуска (run_status, insert_status, run_error) в конце файла
- Кнопки скачивания в 3 местах фронта:
  - История запусков (при status=FAILED)
  - TranslationRunProgress (insert_failed)
  - TranslationRunResult (failed/partial/insert_failed)
- downloadFailedCsv() через fetchApiBlob + Blob URL
2026-06-01 22:38:52 +03:00
40e364d9f6 fix 2026-06-01 17:09:40 +03:00