Files
ss-tools/specs/028-llm-datasource-supeset/tasks.md
busya 46d89c9006 docs(028): update all feature documents for enhancement phase completion (US11-US12)
- effort-estimate-report.md: updated metrics (~182 files, ~40K LOC), added enhancement breakdown, updated comparative analysis
- spec.md: status → Core + Enhancement complete, added Enhancement Implementation Notes
- plan.md: all enhancement components marked , metrics updated
- tasks.md: all 26 enhancement tasks (T135-T160) → [x], closure summary updated
- quickstart.md: added §10 Direct Database Insert flow
- data-model.md, research.md, contracts/modules.md, ux_reference.md, spec.ru.md, checklists/requirements.md: dates, statuses, metrics aligned
- all documents now reflect 2026-06-11 state: ~174-182 files, ~39-40K LOC, ~580 pytest, ~68 vitest
2026-06-11 19:11:05 +03:00

689 lines
76 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# Tasks: LLM Table Translation Service
**Feature Branch**: `028-llm-datasource-supeset` (актуальная: `032-translate-requests-httpx`)
**Input**: Design documents from `/specs/028-llm-datasource-supeset/`
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/modules.md
**Tests**: Test tasks are included for all C4/C5 backend contracts, new API endpoints, and Svelte components with `@UX_STATE` contracts. Test work traces to contract `@PRE`/`@POST` guarantees and spec acceptance scenarios.
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
## Format: `[ID] [P?] [Story] Description`
- **[P]**: Can run in parallel (different files, no dependencies)
- **[Story]**: Which user story this task belongs to (e.g., US1, US5)
- Include exact file paths in descriptions
---
## Phase 1: Setup (Shared Infrastructure)
**Purpose**: Create plugin directory structure and register the new route module in the lazy-import registry.
- [x] T001 Create translation plugin directory structure: `backend/src/plugins/translate/__init__.py`, `backend/src/plugins/translate/plugin.py` (empty skeleton), plus `backend/src/plugins/translate/__tests__/__init__.py`
- [x] T002 Register `translate` route module in `backend/src/api/routes/__init__.py` — add `"translate"` to `__all__` list inside `[DEF:Route_Group_Contracts:Block]`
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: ORM models, Pydantic schemas, plugin boilerplate, route skeleton, and database migration. ALL user stories depend on these artifacts.
**⚠️ CRITICAL**: No user story work can begin until this phase is complete.
### ORM Models
- [x] T003 [P] Create all SQLAlchemy ORM models in `backend/src/models/translate.py`: `TranslationJob`, `TranslationRun`, `TranslationBatch`, `TranslationRecord`, `TranslationEvent`, `TranslationPreviewSession`, `TranslationPreviewRecord`, `TerminologyDictionary`, `DictionaryEntry`, `TranslationSchedule`, `TranslationJobDictionary`, `MetricSnapshot`. Follow patterns from `backend/src/models/llm.py` (UUID PKs, `generate_uuid`, `Base` inheritance, JSON columns, `UniqueConstraint`, indexes, timezone-aware DateTime with callable defaults). Include `source_term_normalized` column on `DictionaryEntry` with unique constraint for case-insensitive matching.
- [x] T004 [P] Create Pydantic v2 request/response schemas in `backend/src/schemas/translate.py`: `TranslateJobCreate`, `TranslateJobUpdate`, `TranslateJobResponse`, `DictionaryCreate`, `DictionaryImport`, `DictionaryResponse`, `TermCorrectionSubmit`, `ScheduleConfig`, `TranslationRunResponse`, `TranslationPreviewResponse` (with `PreviewRow`), `MetricsResponse`. Follow existing `backend/src/schemas/` patterns (use `BaseModel`, `Field` with defaults/validation)
### Plugin Skeleton
- [x] T005 Create `TranslatePlugin` class in `backend/src/plugins/translate/plugin.py` inheriting from `PluginBase`. Implement `id`, `name`, `description` properties. Wire `@RELATION INHERITS -> [PluginBase:Class]` in contract header. (RATIONALE: separate plugin avoids bloating `LLMAnalysisPlugin` beyond fractal limit; REJECTED: extending LLMAnalysisPlugin would conflate domains)
### Route Skeleton
- [x] T006 Create `backend/src/api/routes/translate.py` with FastAPI `APIRouter` (prefix=`/api/translate`, tags=`["translate"]`). Define all endpoint stubs with `pass` bodies for now: CRUD jobs, CRUD dictionaries, preview trigger, run trigger, retry, schedule CRUD, run history, metrics, correction submission, dictionary import. Attach `Depends(require_permission(...))` annotations. Register router in `backend/src/app.py` alongside existing routers.
### Database Migration
- [x] T007 Generate Alembic migration for all `translate_*` tables: `translation_jobs`, `translation_runs`, `translation_records`, `translation_events`, `terminology_dictionaries`, `dictionary_entries`, `translation_schedules`, `translation_job_dictionaries`. Run `cd backend && alembic revision --autogenerate -m "add translation tables"` and `alembic upgrade head`.
### RBAC Registration
- [x] T008 Register 13 permission strings in the RBAC seed/permission store: `translate.job.view`, `translate.job.create`, `translate.job.edit`, `translate.job.delete`, `translate.job.execute`, `translate.dictionary.view`, `translate.dictionary.create`, `translate.dictionary.edit`, `translate.dictionary.delete`, `translate.schedule.view`, `translate.schedule.manage`, `translate.history.view`, `translate.metrics.view`. Ensure admin role gets all; analyst role gets `translate.job.view`, `translate.job.execute`, `translate.dictionary.view`, `translate.history.view`. Update role seeding script if needed.
**Checkpoint**: Foundation ready — models, schemas, plugin, routes, migration, and RBAC all in place. User story implementation can now begin.
---
## Phase 3: User Story 1 — Configure Translation Job (Priority: P1) 🎯 MVP
**Goal**: User can create, edit, delete, and list translation jobs with datasource selection, column mapping, key columns, target table configuration, LLM settings, and dictionary attachment.
**Independent Test**: Open Configuration form → select Superset datasource → pick translation/context/key columns → specify target table → save → verify job appears in list with correct settings.
### Backend — Job CRUD
- [x] T009 [P] [US1] Implement job CRUD service in `backend/src/plugins/translate/plugin.py` as methods on the `TranslatePlugin` class: `create_job()`, `update_job()`, `delete_job()`, `get_job()`, `list_jobs()`, `duplicate_job()`. Validate column existence via `SupersetClient` on create/update (FR-001, FR-002, FR-006). Enforce composite key support (FR-004). Detect virtual columns and warn (US1 acceptance scenario 5).
- [x] T010 [US1] Implement `/api/translate/jobs` endpoints in `backend/src/api/routes/translate.py`: `POST /` (create), `GET /` (list), `GET /{job_id}` (get), `PUT /{job_id}` (update), `DELETE /{job_id}` (delete), `POST /{job_id}/duplicate` (duplicate — FR-021). Inject `Depends(require_permission("translate.job.*"))` per operation.
- [x] T011 [US1] Implement `/api/translate/datasources/{datasource_id}/columns` endpoint that queries Superset for column metadata (name, type, is_physical flag) and the database dialect (backend/engine) from the connection configuration. Returns column list AND `database_dialect` field for the frontend. Cache dialect on `TranslationJob.database_dialect` at save time. Reject unsupported dialects at configuration time (FR-002, dialect detection).
### Frontend — Job Config UI
- [x] T012 [P] [US1] Create `TranslateApiClient` module in `frontend/src/lib/api/translate.js`: `fetchJobs()`, `createJob()`, `updateJob()`, `deleteJob()`, `duplicateJob()`, `fetchDatasourceColumns()`. Use existing `requestApi`/`fetchApi` wrapper pattern.
- [x] T013 [US1] Create `TranslationJobList` SvelteKit page in `frontend/src/routes/translate/+page.svelte`: list all jobs with name, datasource, status/schedule indicators, create button, duplicate action. `@UX_STATE`: idle, loading, empty, populated, error.
- [x] T014 [US1] Create `TranslationJobConfig` SvelteKit page in `frontend/src/routes/translate/[id]/+page.svelte`: datasource dropdown → column selectors (translation column, context columns, key columns with [+ Add key] for composite), target table/column inputs, LLM provider selector, target language, batch size, prompt template editor, dictionary attachment (multi-select with priority ordering). `@UX_STATE`: idle, loading, configured, saving, validation_error, datasource_unavailable. `@UX_REACTIVITY`: column list `$derived` from datasource selection.
### Verification — US1
- [x] T015 [US1] Write pytest integration tests for job CRUD API in `backend/tests/test_translate_jobs.py`: test create with valid config, create with missing translation column (expect 422), create with virtual key column (expect warning), update job, delete job, duplicate job. Mock `SupersetClient` for column metadata.
- [x] T016 [US1] Verify US1 acceptance scenarios against `specs/028-llm-datasource-supeset/spec.md` User Story 1 (5 scenarios). Run `cd backend && pytest backend/tests/test_translate_jobs.py -v`.
**Checkpoint**: Job CRUD fully functional — user can create, edit, list, and duplicate translation jobs with validated column mappings.
---
## Phase 4: User Story 5 — Terminology Dictionary Management (Priority: P2)
**Goal**: User can create, edit, delete dictionaries; add terms inline; import CSV/TSV with duplicate detection; attach dictionaries to jobs with priority ordering.
**Independent Test**: Create dictionary with 5 terms → import CSV with 50 terms → verify duplicates flagged → attach dictionary to job → verify dictionary appears in job config.
### Backend — Dictionary CRUD + Import
- [x] T017 [P] [US5] Implement `DictionaryManager` class in `backend/src/plugins/translate/dictionary.py`: `create_dictionary()`, `update_dictionary()`, `delete_dictionary()`, `get_dictionary()`, `list_dictionaries()`, `add_entry()`, `edit_entry()`, `delete_entry()`, `clear_entries()`. Enforce unique `source_term` per dictionary with conflict resolution (FR-026). Prevent deletion if attached to active/scheduled jobs (FR-030). `@COMPLEXITY 4` — instrument with `belief_scope`/`reason`/`reflect` markers at mutation boundaries. (RATIONALE: C4 warranted because dictionary CRUD is stateful and must enforce referential integrity on deletion; REJECTED: pure C3 CRUD without state guards would allow orphaned job-dictionary links)
- [x] T018 [US5] Implement CSV/TSV import in `DictionaryManager`: parse uploaded content, detect delimiter, create `DictionaryEntry` rows, preview with duplicate detection, return parse errors with line numbers for malformed rows (FR-025). Add `DictionaryImport` schema validation.
- [x] T019 [US5] Implement `/api/translate/dictionaries` endpoints in `backend/src/api/routes/translate.py`: `POST /` (create), `GET /` (list), `GET /{dict_id}` (get with entries), `PUT /{dict_id}` (update), `DELETE /{dict_id}` (delete — blocked if attached), `POST /{dict_id}/entries` (add entry), `PUT /{dict_id}/entries/{entry_id}` (edit), `DELETE /{dict_id}/entries/{entry_id}` (delete), `POST /{dict_id}/import` (CSV/TSV import with preview).
- [x] T020 [US5] Implement per-batch dictionary filtering logic in `DictionaryManager.filter_for_batch(source_texts: list[str]) -> list[dict]`: scan batch texts for substrings matching dictionary `source_term` values; return matched entries in priority order across all attached dictionaries (FR-044). This is consumed by US2 (preview) and US3 (executor).
### Frontend — Dictionary UI
- [x] T021 [P] [US5] Add dictionary API methods to `frontend/src/lib/api/translate.js`: `fetchDictionaries()`, `createDictionary()`, `updateDictionary()`, `deleteDictionary()`, `fetchDictionaryEntries()`, `addEntry()`, `editEntry()`, `deleteEntry()`, `importDictionary()`.
- [x] T022 [US5] Create `DictionaryList` SvelteKit page in `frontend/src/routes/translate/dictionaries/+page.svelte`: list dictionaries with name, language, term count, attached job count, create/delete actions. `@UX_STATE`: idle, loading, empty, populated, delete_blocked.
- [x] T023 [US5] Create `DictionaryEditor` SvelteKit page in `frontend/src/routes/translate/dictionaries/[id]/+page.svelte`: inline term editor (source_term → target_translation), add/delete rows, CSV/TSV import with conflict preview, export. `@UX_STATE`: idle, loading, editing, importing, import_preview, import_conflict, saving. `@UX_FEEDBACK`: import preview with duplicate flags; toast on save.
### Verification — US5
- [x] T024 [US5] Write pytest tests for DictionaryManager in `backend/src/plugins/translate/__tests__/test_dictionary.py`: test create/update/delete, add entry with duplicate detection (expect conflict), import CSV with valid/invalid rows, delete dictionary blocked by active job, per-batch filtering returns matched terms.
- [x] T025 [US5] Verify US5 acceptance scenarios against spec User Story 5 (6 scenarios). Run `cd backend && pytest backend/src/plugins/translate/__tests__/test_dictionary.py -v`.
**Checkpoint**: Dictionary management fully functional — CRUD, import, filtering, and job attachment all work.
---
## Phase 5: User Story 2 — Preview Translated Output (Priority: P2)
**Goal**: User triggers preview on a saved job → system fetches sample rows → sends to LLM with context + dictionary → displays source/context/translation side-by-side → user approves/edits/rejects → preview state saved for execution gate.
**Independent Test**: Create job + dictionary → click Preview → verify 10 rows shown with LLM translations → approve 8, edit 1, reject 1 → verify state preserved.
### Backend — Preview Engine
- [x] T026 [US2] Implement `TranslationPreview` class in `backend/src/plugins/translate/preview.py`: `preview_rows(job_id, sample_size)`. Fetch source rows from Superset via `SupersetClient`; construct LLM prompt using `LLMProviderService` + `llm_prompt_templates.render_prompt()` + `DictionaryManager.filter_for_batch()`; call LLM; return `PreviewRow` list. `@COMPLEXITY 4` — instrument with `belief_scope`/`reason`/`reflect` at LLM call boundaries. (RATIONALE: C4 because preview is stateful (approve/edit/reject lifecycle) and calls external LLM API with side effects; REJECTED: making preview purely read-only without approval state would degrade UX by losing user decisions between preview and execution)
- [x] T027 [US2] Implement token count and cost estimation in preview response: compute estimated tokens from sample → extrapolate to full dataset row count → apply provider pricing → return `estimated_total_rows`, `estimated_tokens`, `estimated_cost` in `TranslationPreviewResponse` (FR-014).
- [x] T028 [US2] Implement preview quality gate: create persistent `TranslationPreviewSession` and `TranslationPreviewRecord` rows with `config_hash` and `dict_snapshot_hash`. Preview acceptance gates full execution; rejected preview sample rows are excluded from full run. Preview is a quality gate — unseen rows are processed normally in full run.
- [x] T029 [US2] Implement `/api/translate/jobs/{job_id}/preview` endpoint: `POST` triggers preview, returns preview rows with `status=pending`. Add `PUT /api/translate/jobs/{job_id}/preview/rows/{row_key}` for approve/edit/reject actions. Add `POST /api/translate/jobs/{job_id}/preview/approve-all` for bulk approve.
### Frontend — Preview UI
- [x] T030 [P] [US2] Add preview API methods to `frontend/src/lib/api/translate.js`: `fetchPreview()`, `approveRow()`, `editRow()`, `rejectRow()`, `approveAll()`.
- [x] T031 [US2] Create `TranslationPreview` component in `frontend/src/lib/components/translate/TranslationPreview.svelte`: side-by-side table (source, context, LLM translation), approve/edit/reject buttons per row, bulk approve, cost estimate card before full run, row limit input. `@UX_STATE`: idle, loading, preview_loaded, preview_error, retrying. `@UX_FEEDBACK`: spinner during LLM call; visual distinction for LLM-generated vs user-edited values; cost estimate reactivity. `@UX_RECOVERY`: retry preview button; individual row re-translate.
- [x] T032 [US2] Integrate `TranslationPreview` into `TranslationJobConfig` page (`frontend/src/routes/translate/[id]/+page.svelte`) as a tab or collapsible section that appears after job is saved.
### Verification — US2
- [x] T033 [US2] Write pytest tests for preview in `backend/src/plugins/translate/__tests__/test_preview.py`: test preview with valid job, preview with dictionary (verify glossary terms in prompt), preview row approve/edit/reject state transitions, cost estimation accuracy. Mock LLM provider responses.
- [x] T034 [US2] Write vitest component test for `TranslationPreview` in `frontend/src/lib/components/translate/__tests__/TranslationPreview.test.js`: test rendering of preview rows, approve/reject/edit interactions, bulk approve behavior. Mock API client.
- [x] T035 [US2] Verify US2 acceptance scenarios against spec User Story 2 (5 scenarios). Run `cd backend && pytest backend/src/plugins/translate/__tests__/test_preview.py -v && cd frontend && npm run test -- --run`.
**Checkpoint**: Preview flows complete — LLM translation with context + dictionary, approve/edit/reject lifecycle, cost estimation.
---
## Phase 6: User Story 3 — Execute Translation & Insert Results (Priority: P3)
**Goal**: User triggers full batch execution → system processes rows in batches → generates INSERT SQL → user copies to SQL Lab or auto-executes → failed batches retryable.
**Independent Test**: Create job → preview + approve → execute → verify INSERT SQL generated with correct key columns → execute in SQL Lab → verify rows in target table.
### Backend — Executor + SQL Generator + Orchestrator
- [x] T036 [US3] Implement `SQLGenerator` class in `backend/src/plugins/translate/sql_generator.py`: `generate_insert(records: list[TranslationRecord], job: TranslationJob) -> str`. Detect dialect from `job.database_dialect` (cached from Superset connection at save time). Produce safe dialect-appropriate SQL: for PostgreSQL/Greenplum — `INSERT INTO "target_table" ("key_cols"..., "target_col") VALUES (...)` with quoted identifiers; support `upsert_strategy`: `insert` (plain INSERT), `skip_existing` (ON CONFLICT DO NOTHING), `overwrite` (ON CONFLICT DO UPDATE). For ClickHouse — `INSERT INTO target_table (key_cols..., target_col) VALUES (...)`; `skip_existing` warns user (not natively supported); `overwrite` documented limitation. `@COMPLEXITY 3`. (RATIONALE: dialect-aware because Superset connections may use ClickHouse or PostgreSQL; REJECTED: PostgreSQL-only would break ClickHouse users; raw identifier interpolation rejected)
- [x] T037 [US3] Implement `TranslationExecutor` class in `backend/src/plugins/translate/executor.py`: `execute_run(run: TranslationRun, job: TranslationJob)`. Fetch all source rows from Superset; split into batches; for each batch: call `DictionaryManager.filter_for_batch()`, construct prompt via `LLMProviderService`, call LLM, create `TranslationRecord` rows with status `translated`/`failed`/`skipped`; handle batch-level retry on LLM failure (FR-015); skip NULL translation values (FR-016); reject NULL key values (FR-017); update run statistics. `@COMPLEXITY 4` — instrument with `belief_scope`/`reason`/`reflect` at batch boundaries and error paths.
- [x] T038 [US3] Implement `TranslationOrchestrator` class in `backend/src/plugins/translate/orchestrator.py`: `start_run(job_id, trigger_type)`. Validate preconditions (job config valid, datasource accessible, LLM provider reachable); create `TranslationRun` with status `running` and config/dict snapshots (FR-019, FR-029); dispatch to `TranslationExecutor`; on completion call `SQLGenerator`; record `TranslationEvent` rows via `TranslationEventLog` (FR-046); enforce state transitions: pending → running → (completed | partial | failed) — no skipping. `@COMPLEXITY 5` — full `@PRE`/`@POST`/`@DATA_CONTRACT`/`@INVARIANT` enforcement with `@RATIONALE`/`@REJECTED`. (RATIONALE: central coordinator is C5 because preview, execution, event logging, and retry share run state and must coordinate within a single transaction boundary; REJECTED: distributed actor model would introduce eventual-consistency challenges for status tracking at current scale)
- [x] T039 [US3] Implement `TranslationEventLog` class in `backend/src/plugins/translate/events.py`: `log_event(run_id, job_id, event_type, payload)`. Create immutable `TranslationEvent` row. `query_events(job_id, filters)` for audit/dashboard. `prune_expired()` for 90-day retention enforcement (FR-049) — scheduled via APScheduler cleanup job. `@COMPLEXITY 5``@INVARIANT`: every run must have exactly one `run_started` and one terminal event. (RATIONALE: C5 warranted because event log is single source of truth for observability, metrics, and audit; REJECTED: stdout-only logging lacks structured payload integrity and cannot enforce terminal-event invariant)
- [x] T040 [US3] Implement execution endpoints in `backend/src/api/routes/translate.py`: `POST /api/translate/jobs/{job_id}/runs` (trigger manual run — creates run, dispatches orchestrator which translates AND submits to Superset API), `GET /api/translate/runs/{run_id}` (status + statistics + insert_status + superset_query_id), `GET /api/translate/runs/{run_id}/records` (paginated TranslationRecord list), `POST /api/translate/runs/{run_id}/retry` (retry failed batches only — FR-015), `POST /api/translate/runs/{run_id}/retry-insert` (retry Superset insert only without re-translating). Inject `Depends(require_permission("translate.job.execute"))`.
### Frontend — Execution UI
- [x] T041 [P] [US3] Add execution API methods to `frontend/src/lib/api/translate.js`: `triggerRun()`, `fetchRunStatus()`, `fetchRunRecords()`, `retryFailedBatches()`.
- [x] T042 [US3] Create `TranslationRunProgress` component in `frontend/src/lib/components/translate/TranslationRunProgress.svelte`: live progress bar (WebSocket-driven from `TaskWebSocket`), batch counter (N/M), success/failure/skip counts, cancel button. `@UX_STATE`: idle, running, pausing, cancelled, completed, partial, failed. `@UX_FEEDBACK`: progress percentage `$derived` from translated/total; real-time counts. `@UX_RECOVERY`: retry failed batches button; cancel run; download skipped rows.
- [x] T043 [US3] Create `TranslationRunResult` component in `frontend/src/lib/components/translate/TranslationRunResult.svelte`: completion summary (rows translated/failed/skipped, token count, cost, insert_status), Superset execution reference with status badge, generated SQL block for audit/debugging (collapsed by default), retry-insert button. `@UX_STATE`: completed, partial, failed, insert_failed. `@UX_FEEDBACK`: Superset execution status badge; SQL block for audit.
- [x] T044 [US3] Integrate `TranslationRunProgress` and `TranslationRunResult` into `TranslationJobConfig` page as the "Run" tab/section.
### Verification — US3
- [x] T045 [US3] Write pytest tests for `SQLGenerator` in `backend/src/plugins/translate/__tests__/test_sql_generator.py`: test INSERT with single key, composite key — for PostgreSQL dialect AND ClickHouse dialect. Test PostgreSQL UPSERT (ON CONFLICT DO NOTHING, ON CONFLICT DO UPDATE). Test ClickHouse plain INSERT and skip_existing warning. Test NULL key rejection, NULL translation value skipping, identifier quoting per dialect, injection safety. Validate SQL syntax correctness against each dialect.
- [x] T046 [US3] Write pytest tests for executor + orchestrator in `backend/src/plugins/translate/__tests__/test_orchestrator.py`: test full run lifecycle (pending→running→completed), partial failure (one batch fails, rest succeed), batch retry, event log invariants, NULL handling. Mock LLM provider and SupersetClient.
- [x] T047 [US3] Verify US3 acceptance scenarios against spec User Story 3 (5 scenarios). Run `cd backend && pytest backend/src/plugins/translate/__tests__/test_orchestrator.py backend/src/plugins/translate/__tests__/test_sql_generator.py -v`.
**Checkpoint**: Execution pipeline complete — batch processing, INSERT generation, retry, event logging. User can translate data and insert into target table.
---
## Phase 7: User Story 6 — Feedback Loop (Correct → Dictionary) (Priority: P3)
**Goal**: In run results, user selects incorrect translation → submits correction to dictionary → dictionary updated with origin tracking → next run uses corrected term.
**Independent Test**: Complete a run → find incorrect translation → open correction popup → submit to dictionary → re-run preview → verify corrected term used.
### Backend — Correction Submission
- [x] T048 [US6] Implement correction submission endpoint in `backend/src/api/routes/translate.py`: `POST /api/translate/corrections` accepting `TermCorrectionSubmit` body. Validate target language match between dictionary and job (FR language validation edge case); detect existing entry conflict → return conflict response (FR-032); create `DictionaryEntry` with origin tracking (`origin_run_id`, `origin_row_key`, `origin_user_id`) per FR-033. Inject `Depends(require_permission("translate.dictionary.edit"))`.
- [x] T049 [US6] Implement bulk correction endpoint: `POST /api/translate/corrections/bulk` accepting array of `TermCorrectionSubmit` objects (FR-034). Process atomically — if any conflict is detected, return all conflicts for user resolution before partial apply.
### Frontend — Correction UI
- [x] T050 [P] [US6] Add correction API methods to `frontend/src/lib/api/translate.js`: `submitCorrection()`, `submitBulkCorrections()`.
- [x] T051 [US6] Create `TermCorrectionPopup` component in `frontend/src/lib/components/translate/TermCorrectionPopup.svelte`: text selection on source term and incorrect target translation → popup with source term (pre-filled from source column), incorrect target translation (pre-filled from selection), corrected target translation input, dictionary selector dropdown (filtered by target language), submit button, conflict dialog (overwrite/keep existing/cancel). `@UX_STATE`: closed, selecting, editing, submitting, conflict_detected, submitted. `@UX_FEEDBACK`: "Added to Dictionary" badge on corrected row.
- [x] T052 [US6] Create `BulkCorrectionSidebar` component in `frontend/src/lib/components/translate/BulkCorrectionSidebar.svelte`: sidebar collecting selected terms across rows, per-term correction inputs, submit all to dictionary. `@UX_STATE`: closed, collecting, reviewing, submitting, submitted. `@UX_REACTIVITY`: selected terms list `$state`.
- [x] T053 [US6] Integrate feedback-loop components into `TranslationRunResult` (T043) — add selection highlight behavior and correction triggers.
### Verification — US6
- [x] T054 [US6] Write pytest tests for correction endpoints in `backend/tests/test_translate_corrections.py`: test single correction, bulk correction, conflict detection (existing term), cross-language rejection, origin tracking fields populated. Verify corrected term appears in next preview's dictionary filter.
- [x] T055 [US6] Verify US6 acceptance scenarios against spec User Story 6 (5 scenarios). Run `cd backend && pytest backend/tests/test_translate_corrections.py -v`.
**Checkpoint**: Feedback loop complete — corrections flow from results → dictionary → next run.
---
## Phase 8: User Story 10 — Schedule Translation Jobs (Priority: P3)
**Goal**: User configures schedule → system triggers runs → new-key-only translation → optional auto-INSERT → failure notification → pause/resume.
**Independent Test**: Configure schedule (every 5 min for test) → wait for trigger → verify new TranslationRun created → verify only new keys translated → disable schedule → verify no more triggers.
### Backend — Schedule Management + Trigger Dispatch
- [x] T056 [US10] Implement `TranslationScheduler` class in `backend/src/plugins/translate/scheduler.py`: `create_schedule()`, `update_schedule()`, `delete_schedule()`, `enable_schedule()`, `disable_schedule()`, `get_next_executions(schedule, n=3)` (FR-036). Register schedule with existing `SchedulerService` via `add_job()` with cron/interval/date trigger. `@COMPLEXITY 4` — instrument with `belief_scope`/`reason`/`reflect`. (RATIONALE: C4 because schedule management is stateful with APScheduler integration, concurrency policy enforcement, and trigger dispatch side effects)
- [x] T057 [US10] Implement schedule trigger handler: `_execute_scheduled_translation(job_id)`. Enforce concurrency policy: check if previous run for same job is still `running``skip` (log + event) or `queue` (start after previous completes) per FR-039. If proceeding: create new `TranslationRun` with `trigger_type=scheduled`; fetch source rows; apply new-key-only filter (FR-045) — compare current key values against previous successful run's key values; dispatch to `TranslationOrchestrator`. On failure, send notification via `NotificationService` (FR-041, FR-048). Schedule remains enabled for next trigger (US10 acceptance scenario 6).
- [x] T058 [US10] Implement Superset SQL Lab API submission for all runs: create `SupersetSqlLabExecutor` class in `backend/src/plugins/translate/superset_executor.py`. Submit generated SQL to `/api/v1/sqllab/execute/`, poll execution status, update `TranslationRun.insert_status`, `superset_query_id`, `rows_affected`, error fields. For scheduled runs, this happens automatically; for manual runs, this happens on user trigger. Record `insert_submitted`/`insert_succeeded`/`insert_failed` events.
- [x] T059 [US10] Implement schedule endpoints in `backend/src/api/routes/translate.py`: `PUT /api/translate/jobs/{job_id}/schedule` (create/update), `DELETE /api/translate/jobs/{job_id}/schedule` (remove), `POST /api/translate/jobs/{job_id}/schedule/enable` (FR-040), `POST /api/translate/jobs/{job_id}/schedule/disable` (FR-040). Inject `Depends(require_permission("translate.schedule.manage"))`. Add schedule warning when editing job with active schedule (FR-042).
- [x] T060 [US10] Extend `SchedulerService.load_schedules()` in `backend/src/core/scheduler.py` to discover and register active `TranslationSchedule` rows alongside existing backup schedules (R4).
### Frontend — Schedule UI
- [x] T061 [P] [US10] Add schedule API methods to `frontend/src/lib/api/translate.js`: `updateSchedule()`, `deleteSchedule()`, `enableSchedule()`, `disableSchedule()`.
- [x] T062 [US10] Create `ScheduleConfig` component in `frontend/src/lib/components/translate/ScheduleConfig.svelte`: type selector (cron/interval/once), cron expression input with validation, interval input, timezone selector, run-at datetime picker, next-3-executions preview (with timezone), concurrency policy selector (skip/queue), enable/disable toggle with status indicator. Warns if no prior successful manual run exists. `@UX_STATE`: idle, editing, validating, enabled, disabled, no_prior_run_warning. `@UX_REACTIVITY`: next execution times `$derived` from schedule config with timezone display.
- [x] T063 [US10] Integrate `ScheduleConfig` into `TranslationJobConfig` page as the "Schedule" tab.
### Verification — US10
- [x] T064 [US10] Write pytest tests for scheduler in `backend/src/plugins/translate/__tests__/test_scheduler.py`: test schedule CRUD, cron expression validation, next-N-executions calculation, trigger dispatch with skip/queue concurrency, new-key-only filter (verify only unseen keys processed), auto-INSERT execution, failure notification, pause/resume, load on SchedulerService start.
- [x] T065 [US10] Verify US7 acceptance scenarios against spec User Story 7 (8 scenarios). Run `cd backend && pytest backend/src/plugins/translate/__tests__/test_scheduler.py -v`.
**Checkpoint**: Scheduling complete — jobs can run automatically on schedule with new-key-only incremental translation and failure recovery.
---
## Phase 9: User Story 4 — Translation History & Audit Trail (Priority: P4)
**Goal**: User views past runs with filterable list; inspects run details (config snapshot, prompt, translations, INSERT SQL); sees edit marks; duplicates job. Admin views metrics dashboard.
**Independent Test**: Run several translations → open history → filter by datasource → click run → verify config snapshot, prompt, translations with edit marks, INSERT SQL all shown.
### Backend — History + Metrics Endpoints
- [x] T066 [US4] Implement history endpoints in `backend/src/api/routes/translate.py`: `GET /api/translate/runs` (list with filters: `job_id`, `datasource_id`, `target_table`, `status`, `date_from`, `date_to`, pagination per FR-020), `GET /api/translate/runs/{run_id}` (detail with `config_snapshot`, `prompt_used`, `records` with `llm_translation` and `user_edit` fields visible — FR showing original vs user-edited).
- [x] T067 [US4] Implement `TranslationMetrics` class in `backend/src/plugins/translate/metrics.py`: `get_job_metrics(job_id) -> MetricsResponse`. Aggregate from `TranslationEvent` table: total runs, success/failure counts, cumulative tokens, cumulative cost, average batch latency (FR-047). `@COMPLEXITY 3`.
- [x] T068 [US4] Implement metrics endpoint: `GET /api/translate/jobs/{job_id}/metrics`. Inject `Depends(require_permission("translate.history.view"))`.
### Frontend — History + Metrics UI
- [x] T069 [P] [US4] Add history API methods to `frontend/src/lib/api/translate.js`: `fetchRunHistory()`, `fetchRunDetail()`, `fetchJobMetrics()`.
- [x] T070 [US4] Create `TranslationHistory` SvelteKit page in `frontend/src/routes/translate/history/+page.svelte`: filterable table (datasource, target table, row count, status, date, user), click-to-expand detail with config snapshot, prompt, translation rows with edit marks, INSERT SQL. `@UX_STATE`: idle, loading, empty, populated, detail_open. `@UX_REACTIVITY`: filtered list `$derived` from filters.
- [x] T071 [US4] Create admin metrics dashboard section (integrated into existing admin pages or standalone) displaying per-job metrics: run counts, success/failure ratio, cumulative tokens, cumulative cost, average latency. Use `MetricsResponse` schema.
### Verification — US4
- [x] T072 [US4] Write pytest tests for history + metrics in `backend/tests/test_translate_history.py`: test run list with filters, run detail with snapshots, metrics aggregation accuracy, `TranslationEvent` queryability.
- [x] T073 [US4] Verify US4 acceptance scenarios against spec User Story 4 (4 scenarios). Run `cd backend && pytest backend/tests/test_translate_history.py -v`.
**Checkpoint**: History and audit complete — all runs traceable, metrics dashboard populated.
---
## Phase 10: Polish & Cross-Cutting Concerns
**Purpose**: Retention enforcement, notification wiring, semantic audit, quickstart validation, and rejected-path regression protection.
- [x] T074 [P] Implement 90-day retention pruning in `TranslationEventLog.prune_expired()`: run as APScheduler daily cleanup job. BEFORE pruning events/records: persist cumulative metrics as `MetricSnapshot` row (tokens, cost, run counts). Then prune `TranslationRecord`, `TranslationPreviewRecord`, `TranslationEvent`, and `insert_sql`/`config_snapshot` fields older than 90 days. Preserve `TranslationRun` metadata, `MetricSnapshot` rows, and `superset_query_id`. Verify metrics remain accurate post-prune (SC-014). (RATIONALE: metric snapshots prevent cumulative data loss from event pruning; REJECTED: indefinite retention would violate storage constraints)
- [x] T075 [P] Wire scheduled-run failure notification: ensure `TranslationScheduler` trigger handler calls `NotificationService.send()` when a scheduled run fails (FR-041, FR-048). Test with mock notification provider.
- [x] T076 [P] Instrument remaining C4/C5 Python flows with `belief_scope`/`reason`/`reflect`/`explore` markers where missing: `TranslationOrchestrator.start_run()` (entry/exit), `TranslationExecutor.execute_run()` (batch boundaries + error paths), `DictionaryManager` mutation boundaries, `TranslationScheduler` trigger dispatch. Verify via `axiom_semantic_validation` belief-runtime audit.
- [x] T077 Run full semantic audit via axiom MCP tools:
- `axiom_semantic_validation audit_contracts --file_path backend/src/plugins/translate/` — verify all `#region`/`#endregion` anchors are properly closed, `@RELATION` targets resolve, no orphan contracts, C4+ contracts have required tag density per GRACE complexity scale
- `axiom_semantic_validation audit_belief_protocol --file_path backend/src/plugins/translate/` — verify `@RATIONALE`/`@REJECTED` present on all C5 contracts only (not on C1-C4)
- `axiom_semantic_validation audit_belief_runtime --file_path backend/src/plugins/translate/` — verify `belief_scope`/`reason`/`reflect`/`explore` markers exist in all C4+ module bodies
- `axiom_semantic_validation impact_analysis --contract_id TranslationOrchestrator:Class` — verify no rejected path is accidentally re-enabled
- [x] T078 Run quickstart validation: follow `specs/028-llm-datasource-supeset/quickstart.md` end-to-end — create dictionary → create job → preview → execute → verify INSERT SQL → submit correction → schedule → view history → verify metrics. Run `cd backend && pytest -v`, `cd frontend && npm run test -- --run`, `cd backend && ruff check src/plugins/translate/ src/api/routes/translate.py src/models/translate.py src/schemas/translate.py`.
- [x] T079 Rejected-path regression guard: add a test case in `backend/src/plugins/translate/__tests__/test_orchestrator.py` verifying snapshot isolation — changing job config mid-run does NOT invalidate the running TranslationRun. Add a test case in `backend/src/plugins/translate/__tests__/test_sql_generator.py` verifying that UPDATE statements are never generated (only INSERT/UPSERT per PostgreSQL dialect). Add a test case in `backend/src/plugins/translate/__tests__/test_dictionary.py` verifying that duplicate source_term entries cannot coexist (UniqueConstraint enforced) and conflict resolution only offers overwrite/keep-existing. Add a test case in `backend/src/plugins/translate/__tests__/test_retention.py` verifying metric snapshots are persisted before event pruning and cumulative metrics remain accurate post-prune.
- [x] T080 [P] Implement cancel run endpoint: `POST /api/translate/runs/{run_id}/cancel` in `backend/src/api/routes/translate.py`. Set `translation_status=cancelled`, mark in-progress batches as failed, do NOT submit INSERT SQL. Emit `run_cancelled` event. Inject `Depends(require_permission("translate.job.execute"))`.
- [x] T081 [P] Implement download skipped rows endpoint: `GET /api/translate/runs/{run_id}/skipped.csv` returning CSV of rows skipped due to NULL keys or translation failures. Use `key_hash` for efficient lookup.
- [x] T082 [P] Compute `key_hash` for TranslationRecord and TranslationPreviewRecord: `hash(canonical_json(key_values))` at creation time. Add `config_hash` for TranslationRun and TranslationPreviewSession: hash of effective config (columns, keys, target, prompt, dictionaries). Use for idempotency checks, new-key-only filtering, and stale preview detection.
---
## Phase 11: Multi-Language & Correction Optimization ✅ COMPLETE (2026-05-14)
**Purpose**: Implement multi-language support (auto-detection, multi-target, per-language storage, multilingual dictionaries, improved correction workflow). All tasks below are NEW additions building on the existing single-target foundation.
### Shared Infrastructure (Migration)
- [x] T083 [P] Add `TranslationLanguage`, `TranslationPreviewLanguage`, `TranslationRunLanguageStats` models to `backend/src/models/translate.py`. Add Alembic migration. Existing `TranslationRecord` gains `languages` relationship; existing `TranslationPreviewRecord` gains `languages` relationship. Foreign keys and cascades.
- [x] T084 [P] Add `source_language`, `target_language` columns to `DictionaryEntry` in `backend/src/models/translate.py`. Update unique constraint to `(dictionary_id, source_term_normalized, source_language, target_language)`. Add migration.
- [x] T085 [P] Migrate `TranslationJob.target_language` (str) → `target_languages` (JSON list) in `backend/src/models/translate.py`. Add migration script for existing jobs: `target_languages = [old_target_language]`. Keep `source_language` as optional fallback hint.
- [x] T086 [P] Update Pydantic schemas in `backend/src/schemas/translate.py`: `TranslateJobCreate` gets `target_languages: list[str]` (required, min 1), `source_language` optional. Add `TranslationLanguageResponse`, `TranslationPreviewLanguageResponse`, `BulkFindReplaceRequest`, `InlineCorrectionSubmit` schemas.
### US6 (NEW) — Auto-Detected Source Language
- [x] T087 [P] [US6] Update LLM prompt construction in `backend/src/plugins/translate/executor.py` and `backend/src/plugins/translate/preview.py`: instruct LLM to return `detected_source_language` (BCP-47) alongside each row's translations. Parse and store per-row in `TranslationLanguage.source_language_detected`.
- [x] T088 [P] [US6] Update `TranslationPreview` to display detected source language per row in preview results. Add `detected_source_language` field to preview response. Flag rows with "und" for manual review.
- [x] T089 [P] [US6] Update `TranslationExecutor` to store detected source language per `TranslationLanguage` entry. Handle "und" (undetermined) rows — mark as flagged, allow manual override.
- [x] T090 [US6] Write pytest tests for auto-detection: test with known source languages (en, fr, de, es), test with mixed-language content (expect "und"), test manual override of "und" rows.
### US7 (NEW) — Multilingual Dictionaries
- [x] T091 [P] [US7] Update `DictionaryManager` (`backend/src/plugins/translate/dictionary.py`): add `source_language` and `target_language` to all CRUD operations. Update unique constraint enforcement. Add language-pair filtering to `filter_for_batch()` method.
- [x] T092 [P] [US7] Update dictionary import (`frontend/src/routes/translate/dictionaries/[id]/+page.svelte`): CSV/TSV import now accepts `source_language`, `target_language` columns (or defaults from UI). Add language pair columns to the entry table. Add filter bar by language pair.
- [x] T093 [P] [US7] Update `filter_for_batch()` in `backend/src/plugins/translate/dictionary.py`: accept `source_language` and `target_language` parameters. Only return entries where `source_language` matches row's detected language AND `target_language` matches prompt's target language.
- [x] T094 [P] [US7] Migrate existing single-language dictionaries: each entry gets `source_language = job.source_language or "und"`, `target_language = dictionary.target_language`. Create Alembic migration data script.
- [x] T095 [US7] Write pytest tests for multilingual dictionaries: test language-pair filtering, test migration of old dictionaries, test unique constraint with language pairs, test import with language columns.
### US1 (UPDATED) — Multi-Language Job Configuration
- [x] T096 [P] [US1] Update job configuration endpoints in `backend/src/api/routes/translate/`: `POST/GET/PUT` jobs now handle `target_languages` list. Validate min 1 language. Backward-compatible: single-language requests wrapped into list.
- [x] T097 [US1] Update `TranslationJobConfig` component (`frontend/src/routes/translate/[id]/+page.svelte`): "Target Language" dropdown → multi-select with search. "Include source language as reference" checkbox. Source language field removed.
- [x] T098 [US1] Update `TranslationJobList` component (`frontend/src/routes/translate/+page.svelte`): display target languages as badges/tags. Update job card layout.
### US2 (UPDATED) — Multi-Language Preview
- [x] T099 [P] [US2] Update `TranslationPreview` (`backend/src/plugins/translate/preview.py`): send source text to LLM once, request translations for ALL target languages + detected source language. Parse multi-language response. Create `TranslationPreviewLanguage` rows per language.
- [x] T100 [P] [US2] Update preview endpoints in `backend/src/api/routes/translate/`: accept `sample_size` parameter (1-100, default 10). Return per-language preview data via `TranslationPreviewLanguageResponse`. Include estimated tokens/cost for multi-target.
- [x] T101 [US2] Update `TranslationPreview` component (`frontend/src/lib/components/translate/TranslationPreview.svelte`): dynamic per-language columns. Sample size slider (1-100) with cost warning at >30. Per-language accept button. Added "und" warning badge for undetermined source language.
- [x] T102 [P] [US2] Implement preview edit carry-forward: when preview is accepted with edited rows, store the edited values per-language. On full execution, check for existing edits by `key_hash` + `language_code` and use edited value instead of re-translating.
- [x] T103 [US2] Write pytest tests for multi-language preview: test 2-language preview, test 5-language preview, test sample size boundary (1, 100), test cost estimation accuracy.
### US3 (UPDATED) — Multi-Language Execution
- [x] T104 [P] [US3] Update `TranslationExecutor` (`backend/src/plugins/translate/executor.py`): construct multi-language LLM prompt (one prompt requesting translations for ALL target languages). Parse multi-language JSON response. Create `TranslationLanguage` rows per language per record.
- [x] T105 [P] [US3] Update `TranslationOrchestrator` (`backend/src/plugins/translate/orchestrator.py`): create `TranslationRunLanguageStats` rows per language on run start. Update statistics per language as batches complete. Track per-language token/cost.
- [x] T106 [P] [US3] Update run status endpoints in `backend/src/api/routes/translate/`: return `language_stats` array in run detail response. Include per-language progress in progress events.
- [x] T107 [US3] Update run result rendering in `frontend/src/lib/components/translate/TranslationRunResult.svelte`: show per-language statistics section with `$derived`. Display per-language columns in results table.
- [x] T108 [US3] Write pytest tests for multi-target execution: test 3-language run, test partial failure (one language fails), test single-language backward compatibility, test per-language statistics accuracy.
### US8 (NEW) — Improved Correction & Preview Workflow
- [x] T109 [P] [US8] Implement inline correction endpoint in `backend/src/api/routes/translate/`: `PUT /api/translate/runs/{run_id}/records/{record_id}/languages/{language_code}` — update `final_value`, record `user_edit`. Optional `submit_to_dictionary` flag with `dictionary_id` parameter.
- [x] T110 [P] [US8] Implement `POST /api/translate/runs/{run_id}/bulk-replace` endpoint: accepts `BulkFindReplaceRequest` (find_pattern, regex flag, replacement_text, target_language). Returns affected record list. On apply: updates all matching `TranslationLanguage.final_value`; optionally submits to dictionary.
- [x] T111 [P] [US8] Add `InlineCorrectionService` in `backend/src/plugins/translate/service.py`: handle dictionary submission from correction — validate language pair, detect conflicts, create/update `DictionaryEntry` with origin tracking.
- [x] T112 [P] [US8] Add `BulkFindReplaceService` in `backend/src/plugins/translate/service.py`: pattern matching across run records, preview generation, atomic apply with optional dictionary submission.
- [x] T113 [US8] Create `CorrectionCell` component (`frontend/src/lib/components/translate/CorrectionCell.svelte`): inline editable cell with submit-to-dictionary action. Click → edit → save → "Submit to Dictionary" → popup. Language pair auto-filled.
- [x] T114 [US8] Create `BulkReplaceModal` component (`frontend/src/lib/components/translate/BulkReplaceModal.svelte`): find/replace pattern inputs, regex toggle, preview table, apply button with confirmation.
- [x] T115 [US8] Update preview component: add sample size slider (1-100) to preview trigger. Wire cost warning. No additional changes needed.
- [x] T116 [US8] Add API methods to `frontend/src/lib/api/translate.js`: `inlineEditCorrection()`, `submitCorrectionToDict()`, `bulkFindReplace()`, `bulkReplacePreview()`.
- [x] T117 [US8] Write pytest tests for inline correction: test single correction → dictionary, test duplicate detection, test bulk replace preview accuracy, test bulk replace atomic apply.
- [x] T118 [US8] Write vitest tests for CorrectionCell and BulkReplaceModal components: test edit flow, test submit-to-dictionary, test find/replace preview, test bulk apply.
### US8b (NEW) — Context-Aware Correction & Dictionary [2026-05-14]
**Purpose**: Extend the correction workflow and dictionary with source-row context capture, context editing in popup, usage notes, and context-aware LLM prompt construction.
- [x] T123 [P] [US8b] Add context fields to `DictionaryEntry` in `backend/src/models/translate.py`: `context_data` (JSON, nullable), `usage_notes` (Text, nullable), `has_context` (Boolean, default=False), `context_source` (String, nullable). Generate Alembic migration.
- [x] T124 [P] [US8b] Update `InlineCorrectionService` (`backend/src/plugins/translate/service.py`): when a correction is submitted, auto-capture source row's context column values as `context_data`. Accept optional overrides from request. Accept `usage_notes`. Handle `context_source` tagging.
- [x] T125 [P] [US8b] Create `ContextAwarePromptBuilder` in `backend/src/plugins/translate/prompt_builder.py`: render dictionary entries with context annotations for `has_context=True`. Compute Jaccard similarity between entry `context_data` and incoming row context. Flag >50% overlap as `# PRIORITY`. Cap context at ~500 tokens per entry.
- [x] T126 [P] [US8b] Update dictionary import/export in API and frontend: CSV/TSV import accepts optional `context_data`, `usage_notes` columns. Export includes these fields.
- [x] T127 [P] [US8b] Update `BulkFindReplaceService`: when `submit_to_dictionary_with_context=true`, auto-capture context from first matching row per unique term pair. `context_source="bulk"`. Warn if context varies across matched rows for same term.
- [x] T128 [US8b] Update `CorrectionCell` and `TermCorrectionPopup`: correction popup shows auto-captured context (with Edit/Remove), `usage_notes` textarea, `context_source` badge.
- [x] T129 [US8b] Update `BulkReplaceModal`: add "Submit to dictionary with context" checkbox + "Usage notes (applied to all)" textarea.
- [x] T130 [US8b] Update `DictionaryEditor` page: display `context_data` and `usage_notes` per entry in expandable row with inline editing. Add context_source badges.
- [x] T131 [US8b] Update `filter_for_batch()` in `backend/src/plugins/translate/dictionary.py`: accept `row_context` parameter. Integrate `ContextAwarePromptBuilder` for priority matching.
- [x] T132 [US8b] Write pytest tests for context-aware correction: test context capture (20 tests), context editing/removal, Jaccard similarity matching, priority flagging, 500-token truncation, context_source tagging.
- [x] T133 [US8b] Write vitest tests for context UI: context display in popup, context editing, usage notes, badge display, context removal, submit (6 tests).
- [x] T134 [P] [US8b] Update correction API endpoint schema: `POST /api/translate/corrections` now accepts optional `context_data` (JSON override), `usage_notes` (text), `keep_context` (boolean, default=true). Response includes new context fields. TermCorrectionSubmit now has keep_context: bool = True, endpoint passes it through.
---
## Phase 12: User Story 12 — Database Connection Settings (Priority: P1) 🆕
**Purpose**: Create reusable DB connection configurations in common Settings. This is a prerequisite for direct DB insert (US11).
**Independent Test**: Navigate to Settings → Connections → Add PostgreSQL connection → Test → Save → Verify connection appears in list and is available in translation job insert method selector.
### Backend — Connection Model + Service
- [x] T135 [P] [US12] Create `DatabaseConnection` Pydantic model in `backend/src/core/config_models.py`: `id: str`, `name: str` (unique), `host: str`, `port: int` (default 5432), `database: str`, `username: str`, `password: str` (encrypted at rest), `dialect: str` (Literal["postgresql", "clickhouse", "mysql"]), `extra_params: dict` (optional JSON for sslmode, connect_timeout), `pool_size: int` (default 5), `created_at: datetime`, `updated_at: datetime`. Promote `GlobalSettings.connections` from `list[dict]` to `list[DatabaseConnection]`. Add backward-compat migration: existing `connections: []` stays `[]`. `@RATIONALE`: typed model prevents config corruption from ad-hoc dicts; `@REJECTED`: keeping `list[dict]` was rejected — would require per-field validation in every consumer.
- [x] T136 [US12] Implement `ConnectionService` class in `backend/src/core/connection_service.py`: `create_connection(data)`, `update_connection(id, data)`, `delete_connection(id)` (blocked if referenced by active jobs — FR-072), `get_connection(id)`, `list_connections()`, `test_connection(id)` (opens temp connection, runs `SELECT 1`, returns latency + success/error — FR-071). Password encrypted via `EncryptionManager` on save, decrypted on test, masked in API responses. `@COMPLEXITY 3` — instrument with `belief_scope`/`reason`/`reflect` at mutation and test boundaries.
- [x] T137 [US12] Implement connection CRUD endpoints in `backend/src/api/routes/settings.py`: `GET /api/admin/settings/connections` (list — passwords masked), `POST /api/admin/settings/connections` (create), `PUT /api/admin/settings/connections/{id}` (update — empty password = keep existing), `DELETE /api/admin/settings/connections/{id}` (delete — blocked by ref check), `POST /api/admin/settings/connections/{id}/test` (test connectivity). All endpoints protected by `require_permission("settings.connections.manage")`. Inject `Depends(get_config_manager)`.
- [x] T138 [P] [US12] Register `settings.connections.manage` permission in the RBAC permission store. Ensure admin role gets it. No other roles get it by default.
### Frontend — Connections Tab
- [x] T139 [P] [US12] Add connection API methods to `frontend/src/lib/api.js` (or a new `connections.js`): `fetchConnections()`, `createConnection()`, `updateConnection()`, `deleteConnection()`, `testConnection(id)`. Use existing `fetchApi`/`requestApi` wrapper pattern.
- [x] T140 [US12] Create `ConnectionsTab.svelte` component in `frontend/src/routes/settings/ConnectionsTab.svelte`: connection list table (name, host:port, database, dialect badge, used-by-N counter, created date), "Add Connection" button, inline edit/delete actions. Add/edit form in modal or expandable section: name, host, port, database, username, password (masked), dialect dropdown, extra params (optional textarea), pool size input. "Test Connection" button with result display (success + latency, or error). Delete with dependency warning. `@UX_STATE`: idle, loading, empty, populated, adding, editing, testing, test_success, test_error, deleting, delete_blocked. `@UX_FEEDBACK`: spinner during test; toast on save/delete; dialect badge colors; masked password.
- [x] T141 [US12] Register "Connections" tab in `frontend/src/routes/settings/settings-utils.ts` — add `"connections"` to `SETTINGS_TABS` array. Update `frontend/src/routes/settings/+page.svelte` tab routing to render `ConnectionsTab`.
- [x] T142 [US12] Wire connection data into consolidated settings API: ensure `GET /api/admin/settings/consolidated` returns connections (passwords masked) and `PATCH /api/admin/settings/consolidated` supports updating connections alongside other settings categories.
### Verification — US12
- [x] T143 [US12] Write pytest tests for `ConnectionService` in `backend/tests/test_settings_connections.py`: test create connection (password encryption verified — stored ≠ plaintext), update connection (password unchanged when empty), delete connection blocked by active job reference, test connection success/failure (mock DB driver), list connections (passwords masked in response), dialect validation (reject unsupported), duplicate name rejection.
- [x] T144 [US12] Write vitest test for `ConnectionsTab.svelte` in `frontend/src/lib/__tests__/ConnectionsTab.test.ts`: test render connection list, add connection form, test button states, delete confirmation, masked password display, dialect badge rendering.
- [x] T145 [US12] Verify US12 acceptance scenarios against spec User Story 12 (7 scenarios). Run `cd backend && pytest backend/tests/test_settings_connections.py -v && cd frontend && npm run test -- --run`.
**Checkpoint**: DB connections manageable in Settings — create, edit, test, delete with dependency safety. Ready for US11 to consume.
---
## Phase 13: User Story 11 — Direct Database Insert (Priority: P2) 🆕
**Purpose**: Enable direct DB INSERT execution as an alternative to Superset SQL Lab API. Consumes connections from US12.
**Independent Test**: Configure a DB connection → create translation job with "Direct DB" insert method → execute run → verify rows in target table with no Superset API calls.
### Backend — DB Executor
- [x] T146 [US11] Create `DbExecutor` class in `backend/src/core/db_executor.py`: `execute_sql(connection_id, sql, run_id)` — resolves `DatabaseConnection` from config, opens native driver connection (asyncpg for PostgreSQL, clickhouse-connect for ClickHouse), executes SQL in a transaction, returns `{success: bool, rows_affected: int, execution_time_ms: float, error: str|None}`. Use connection pooling (per-connection pool, `pool_size` from config). Handle dialect-specific parameter binding (PostgreSQL `$1`/`$2`, ClickHouse `%(key)s`). Handle errors: timeout, auth failure, network, permission denied. `@COMPLEXITY 3` — instrument with `belief_scope`/`reason`/`reflect` at connection acquisition, execution, and error boundaries. `@RATIONALE`: separate executor isolates DB-specific logic from translate orchestrator; `@REJECTED`: embedding DB drivers directly in orchestrator would bloat the C5 contract and mix concerns.
- [x] T147 [P] [US11] Add `asyncpg` and `clickhouse-connect` to `backend/requirements.txt`. For development, `asyncpg>=0.29.0` and `clickhouse-connect>=0.7.0`. Add to `backend/.env.example` notes about driver dependencies.
- [x] T148 [US11] Add `insert_method` (Literal["sqllab", "direct_db"], default "sqllab") and `connection_id` (str, nullable — UUID referencing `GlobalSettings.connections` list, NOT a DB FK) fields to `TranslationJob` ORM model in `backend/src/models/translate.py`. Add Alembic migration. Add Pydantic `model_validator` in `TranslateJobCreate`/`TranslateJobUpdate`: if `insert_method == "direct_db"`, `connection_id` MUST be non-null. Add `ConnectionService.validate_connection_refs()` — scans all jobs, verifies referenced connection IDs exist in `GlobalSettings.connections`, logs orphaned references as warnings. Call this on job save and on connection delete.
- [x] T149 [US11] Add `insert_method` (Literal["sqllab", "direct_db"]) and `connection_snapshot` (JSON, nullable — stores `{name, dialect, host, port, database}`, NEVER password) fields to `TranslationRun` ORM model in `backend/src/models/translate.py`. Add Alembic migration. `connection_snapshot` stores non-sensitive connection metadata at run time for audit — survives connection deletion.
- [x] T150 [US11] Update `TranslationOrchestrator` in `backend/src/plugins/translate/orchestrator.py`: after SQL generation, check `job.insert_method`. If `sqllab` — use existing `SupersetSqlLabExecutor`. If `direct_db` — resolve connection from `ConnectionService`, call `DbExecutor.execute_sql()`, record result (insert_status, rows_affected, insert_error_message). Before execution, snapshot connection metadata to `run.connection_snapshot`. `@RATIONALE`: orchestrator dispatch keeps insert-method logic in one place; `@REJECTED`: separate orchestrators per method would duplicate run lifecycle logic.
- [x] T151 [US11] Update Pydantic schemas in `backend/src/schemas/translate.py`: `TranslateJobCreate`/`TranslateJobUpdate` accept `insert_method` (optional, default "sqllab") and `connection_id` (optional). `TranslateJobResponse` includes `insert_method` and `connection_id`. `TranslationRunResponse` includes `insert_method`, `connection_snapshot` (nullable), `insert_status`, `insert_error_message`.
- [x] T152 [US11] Update `/api/translate/jobs/{job_id}/runs` endpoint: validate `connection_id` when `insert_method = direct_db`. Resolve connection dialect and verify it matches job's detected dialect. Include connection info in run response.
### Frontend — Insert Method Selector
- [x] T153 [P] [US11] Add connection API methods to `frontend/src/lib/api/translate.js` (or reuse from US12): `fetchConnections()` — already covered by T139. Update `createJob()`/`updateJob()` to include `insert_method` and `connection_id`.
- [x] T154 [US11] Create `InsertMethodSelector.svelte` component in `frontend/src/lib/components/translate/InsertMethodSelector.svelte`: radio group: "Superset SQL Lab API" (default, shown with existing Superset env info), "Direct Database Connection" (shown with connection dropdown, filtered by compatible dialect). When "Direct DB" selected but no connections exist → inline message with link to Settings → Connections. When connection selected → optional inline test button. `@UX_STATE`: sqllab_selected, direct_db_selected, no_connections, connection_selected, testing_connection, test_success, test_error. `@UX_FEEDBACK`: dialect compatibility badge; connection status indicator; link to Settings when empty.
- [x] T155 [US11] Integrate `InsertMethodSelector` into `ConfigTabForm.svelte` (`frontend/src/lib/components/translate/ConfigTabForm.svelte`) — add it after the target table/column section, before LLM settings. Wire `insert_method` and `connection_id` into the job config form state.
- [x] T156 [US11] Update `TranslationRunResult.svelte` (`frontend/src/lib/components/translate/TranslationRunResult.svelte`): show insert method badge (SQL Lab / Direct DB). For direct DB runs: show connection name, execution time, rows affected. Hide Superset query reference field (empty for direct DB). For SQL Lab runs: keep existing Superset reference display.
### Verification — US11
- [x] T157 [US11] Write pytest tests for `DbExecutor` in `backend/tests/test_db_executor.py`: test PostgreSQL INSERT via asyncpg (mock or testcontainer), test ClickHouse INSERT via clickhouse-connect, test connection timeout, test auth failure, test SQL syntax error (record failure, don't crash), test connection pooling (reuse connections), test dialect-specific parameter binding.
- [x] T158 [US11] Write pytest tests for orchestrator direct DB dispatch in `backend/src/plugins/translate/__tests__/test_orchestrator.py`: extend existing tests with `insert_method=direct_db` scenarios — test successful direct INSERT, test connection not found, test dialect mismatch, test connection_snapshot stored on run, test retry-insert on direct DB failure.
- [x] T159 [US11] Write vitest test for `InsertMethodSelector.svelte`: test radio toggle, connection dropdown filtering, empty state with link to settings, connection test button states.
- [x] T160 [US11] Verify US11 acceptance scenarios against spec User Story 11 (8 scenarios). Run `cd backend && pytest backend/tests/test_db_executor.py backend/src/plugins/translate/__tests__/test_orchestrator.py -v && cd frontend && npm run test -- --run`.
**Checkpoint**: Direct DB insert fully functional — user can choose insert method, execute directly, and audit results.
---
## Dependencies & Execution Order
### Phase Dependencies
- **Setup (Phase 1)**: No dependencies — can start immediately
- **Foundational (Phase 2)**: Depends on Setup — BLOCKS all user stories
- **US1 (Phase 3)**: Depends on Foundational — no dependencies on other stories. **Recommended start after Foundational.**
- **US5 (Phase 4)**: Depends on Foundational — can run in **parallel with US1**. Dictionary filtering will be consumed later.
- **US2 (Phase 5)**: Depends on US1 (needs saved job) + US5 (needs dictionary filtering). Can start once US1 backend is stable.
- **US3 (Phase 6)**: Depends on US1 + US2. Sequential after US2.
- **US6/10 (Phase 7/8)**: Original US6+US10 (corrections, scheduling). Depends on US3.
- **US4 (Phase 9)**: Depends on US3.
- **Polish (Phase 10)**: Depends on all desired user stories.
- **Phase 11 (Multi-Language)** [NEW]:
- **Migration (T083-T086)**: Blocks ALL Phase 11 work — models, schemas, migration scripts MUST be done first
- **US6 auto-detection (T087-T090)**: Depends on migration. No dependencies on other Phase 11 stories.
- **US7 multilingual dict (T091-T095)**: Depends on migration. Can run **in parallel** with US6 auto-detection.
- **US1 updated (T096-T098)**: Depends on migration + US7 (for language-aware dict attachment). Can start after migration.
- **US2 preview (T099-T103)**: Depends on migration + US6 (auto-detection for preview display).
- **US3 execution (T104-T108)**: Depends on migration + US6 + US7. Sequential after preview changes.
- **US8 correction (T109-T118)**: Depends on migration + US3 (needs multi-language run results).
- **US8b context (T123-T134)**: Depends on US8 (inline correction exists) + US7 (dictionary filtering). Can start after T109-T114 are stable. Fits in US8's parallel slot.
- **US4 history (T119-T122)**: Depends on US3 (per-language run data). Can run **in parallel** with US8/US8b.
- **Phase 12 (US12 — Connection Settings)** 🆕: Depends on Foundational (Phase 2) only — can start immediately. **BLOCKS US11.**
- **Phase 13 (US11 — Direct DB Insert)** 🆕: Depends on US12 (needs connections) + US3 (needs orchestrator execution flow). Can start once US12 backend is stable.
### Parallel Opportunities
| Phase | Parallel Tasks | Notes |
|-------|---------------|-------|
| 1 | — | Sequential (only 2 tasks) |
| 2 | T003 ∥ T004 | Models + Schemas in parallel |
| 3 (US1) | T009 ∥ T012 | Backend CRUD ∥ API client |
| 4 (US5) | T017 ∥ T021 | DictionaryManager ∥ API client |
| 5 (US2) | T030 ∥ T031 | API client ∥ Preview component |
| 6 (US3) | T036 ∥ T041 | SQLGenerator ∥ API client |
| 7 (US6) | T050 ∥ T051 ∥ T052 | API client ∥ Popup ∥ Sidebar |
| 8 (US10) | T061 ∥ T062 | API client ∥ ScheduleConfig |
| 9 (US4) | T069 ∥ T070 | API client ∥ History page |
| 10 | T074 ∥ T075 ∥ T076 | Retention, notifications, belief instrumentation |
| 11a (Migration) | T083 ∥ T084 ∥ T085 ∥ T086 | All model/schema changes in parallel [NEW] |
| 11b (US6 + US7) | T087 ∥ T088 ∥ T089 ∥ T091 ∥ T092 ∥ T093 | Auto-detection + multilingual dict in parallel after migration [NEW] |
| 11c (US8) | T109 ∥ T111 ∥ T112 ∥ T113 ∥ T114 | Inline correction backend + frontend components [NEW] |
| 11d (US8b) | T123 ∥ T124 ∥ T125 ∥ T126 ∥ T127 | Context fields, builder, import/export, bulk [NEW] |
| 12 (US12) 🆕 | T135 ∥ T138 ∥ T139 | Connection model, RBAC, API client |
| 13 (US11) 🆕 | T146 ∥ T147 ∥ T153 ∥ T154 | DbExecutor, requirements, API client, InsertMethodSelector |
### Cross-Story Parallelism
After Migration (Phase 11a):
- **US6 auto-detection** and **US7 multilingual dict** can proceed in parallel
- After US3 original + Phase 11 US3: **US8 correction** and **US4 history update** can proceed in parallel
- **US8b context** can proceed **in parallel** with US8 frontend work once T123-T124 merge
---
## Implementation Strategy
### MVP First (US1 Only)
1. Phase 1 + Phase 2 → Foundation
2. Phase 3 (US1) → Job configuration CRUD
3. **STOP and VALIDATE**: User can create, list, edit, delete translation jobs
4. Deploy/demo — partial value (configuration ready, no translation yet)
### Minimum Viable Feature (US1 + US5 + US2 + US3)
1. Foundation → US1 + US5 (parallel) → US2 → US3
2. **STOP and VALIDATE**: End-to-end translation flow works: configure → preview → execute → INSERT
3. This is the core feature — all remaining stories add automation (US10), quality improvement (US6), and visibility (US4)
### Full Feature (All Stories)
1. MVP → US6 + US10 + US4 (parallel after US3) → Polish
2. Scheduled automation, feedback loop, and audit trail all functional
### Multi-Language Extension (Phase 11)
1. **Migration first** → Models, schemas, Alembic, data migration
2. **US6 + US7 in parallel** → Auto-detection + Multilingual dictionaries
3. **US1 US3 sequentially** → Multi-language job config → Preview → Execution
4. **US8 + US4 in parallel** → Inline correction + Per-language history/metrics
5. **Quickstart re-validation** → Update T078 to cover multi-language flow
### Direct DB Insert Enhancement (Phases 1213) 🆕
1. **Phase 12 first** → Database Connection Settings (US12): models, service, API, UI tab, tests. This is a standalone prerequisite.
2. **Phase 13 after Phase 12** → Direct DB Insert (US11): DB executor, orchestrator dispatch, insert method selector UI, tests.
3. **Quickstart updates** → Add direct DB insert flow to quickstart.md.
---
## Notes
- All file paths reference the actual repository structure (`backend/src/`, `frontend/src/`).
- `@COMPLEXITY 4/5` backend contracts require `belief_scope`/`reason`/`reflect` markers — verified in T076.
- `@RATIONALE`/`@REJECTED` tags appear only in C5 contracts (`TranslationOrchestrator`, `TranslationEventLog`) per INV_7.
- Rejected paths are explicitly protected by regression tests in T079.
- `[NEED_CONTEXT]` markers: none — all contract targets resolve to existing or planned modules within this feature.
- The existing `LLMProviderService`, `SupersetClient`, `SchedulerService`, `NotificationService`, and `TaskWebSocket` contracts are reused without modification.
- Quickstart.md (T078) serves as the human-verifiable acceptance test for the full feature. After Phase 11, update quickstart to cover multi-language flow.
- **Migration considerations**:
- `TranslationJob.target_language``target_languages` migration is one-time. Old API consumers sending single `target_language` are backward-compatible (wrapped into list server-side).
- `TerminologyDictionary.target_language` removal: existing dictionaries get entries migrated with `source_language = job.source_language or "und"` and `target_language = dictionary.target_language`.
- `TranslationRecord``TranslationLanguage` migration: existing single-language records get one `TranslationLanguage` entry per record.
- **LLM prompt change**: Multi-target prompts add non-trivial token overhead (~50% for 3 languages). The prompt structure is: "Translate the following text to languages: [ru, en, de]. Return JSON with detected_source_language and translations keyed by language code."
- **Context-aware prompt format** (US8b):
- Entry without context: `"Voiture" → "Car"`
- Entry with context: `"Voiture" (context: Category=Automotive, Type=Engine) → "Car"`
- Entry with context + notes: `"Voiture" (context: Category=Automotive) → "Car" # Usage: Only for passenger vehicles`
- Priority entry: `"Voiture" (context: Category=Automotive) → "Car" # PRIORITY - context match: 75%`
- Prompt construction caps context at ~500 tokens/entry. Priority entries listed before non-priority entries within the same language pair block.
- **Context similarity heuristic**: Jaccard similarity on tokenized (lowercased, stopwords-removed) text of context column values. Threshold 0.5. Configurable via job settings. Stored per-run in config snapshot.
---
## ✅ Spec 028 Final Closure Summary (2026-06-11)
All 160 tasks complete (134 core + 26 enhancement). Enhancement Phase (US11US12) ✅ COMPLETE.
### Финальная статистика реализации
| Метрика | Значение |
|---------|----------|
| **Всего файлов** | **~180** (бэкенд: 78 source + 43 test, фронтенд: 30 source + 10 test, миграции: 7) |
| **Всего строк кода** | **~40 000** |
| **Бэкенд Python** | ~30 000 строк |
| **Фронтенд Svelte/JS/TS** | ~9 000 строк |
| **Backend tests** | ~580 pytest functions |
| **Frontend tests** | ~68 vitest functions |
| **User stories** | 12 (US1US12) |
| **Enhancement tasks** | 26 (T135T160) ✅ |
| **RBAC permissions** | 14 (+1: settings.connections.manage) |
### Enhancement: Direct DB Insert + Connection Settings (Phases 1213)
| Компонент | Новые/Изменённые файлы | Complexity |
|-----------|----------------------|:----------:|
| `DatabaseConnection` (Pydantic) ✅ | `config_models.py` | C1 |
| `ConnectionService` ✅ | `core/connection_service.py` (NEW) | C3 |
| `DbExecutor` ✅ | `core/db_executor.py` (NEW) | C3 |
| Connection CRUD endpoints ✅ | `api/routes/settings.py` (+5 endpoints) | C3 |
| `TranslationJob` (ORM update) ✅ | `models/translate.py` (+insert_method, +connection_id) | C2 |
| `TranslationRun` (ORM update) ✅ | `models/translate.py` (+insert_method, +connection_snapshot) | C2 |
| `TranslationOrchestrator` (update) ✅ | `plugins/translate/orchestrator.py` (dispatch logic) | C5 |
| `ConnectionsTab.svelte` ✅ | `routes/settings/ConnectionsTab.svelte` (NEW) | C2 |
| `InsertMethodSelector.svelte` ✅ | `components/translate/InsertMethodSelector.svelte` (NEW) | C1 |
### Verified (core) ✅
- Backend: ~580 pytest ✅
- Frontend: ~68 vitest ✅
- Enhancement verified: DbExecutor, ConnectionService, InsertMethodSelector ✅
### Remaining (enhancement) ✅
None. All 26 enhancement tasks are [x].
### Next Action
- `ready_for_review` — Spec 028 is complete, ready for merge to main
### Структура файлов (полный инвентарь)
#### Backend plugin source (59 файлов, `backend/src/plugins/translate/`, 10 176 строк)
| Группа | Файлов | Строк | C | Назначение |
|--------|:-----:|:-----:|:--:|------------|
| Orchestrator | 14 | 1 631 | C5 | Run lifecycle coordinator (декомпозирован) |
| Executor / LLM | 9 | 2 196 | C4 | Batch LLM + caching + async HTTP |
| Preview | 11 | 1 291 | C4 | Preview engine (декомпозирован) |
| Dictionary | 7 | 921 | C4 | CRUD + import + filter |
| Service | 6 | 1 281 | C4 | Job CRUD + InlineCorrection + BulkReplace + target_schema |
| Standalone | 12 | 2 854 | C2-C4 | Scheduler, SQL, SupersetExecutor, PromptBuilder, Events, Metrics, TokenBudget, Utils, LangDetect, TextCleaner |
#### Backend routes (13 файлов, `backend/src/api/routes/translate/`, 2 296 строк)
| Файл | Строк | Назначение |
|------|:-----:|------------|
| `__init__.py` | 42 | Package init + re-export |
| `_router.py` | 13 | APIRouter instance |
| `_job_routes.py` | 285 | Job CRUD |
| `_run_routes.py` | 205 | Run execution, cancel, inline edit |
| `_run_list_routes.py` | 359 | Run list, detail, CSV |
| `_run_edit_routes.py` | 200 | Run edit endpoints [NEW] |
| `_run_history_routes.py` | 133 | Run history [NEW] |
| `_preview_routes.py` | 194 | Preview |
| `_dictionary_routes.py` | 391 | Dictionary CRUD + import |
| `_correction_routes.py` | 100 | Correction submission |
| `_schedule_routes.py` | 239 | Schedule CRUD |
| `_metrics_routes.py` | 65 | Metrics |
| `_helpers.py` | 70 | Response helpers |
#### Frontend компоненты (14 файлов)
| Файл | Строк | C | Назначение |
|------|:-----:|:-:|------------|
| `TranslationPreview.svelte` | 564 | C4 | Preview table |
| `TranslationRunResult.svelte` | 618 | C4 | Run results |
| `ConfigTabForm.svelte` | 539 | C3 | Configuration form [NEW] |
| `BulkReplaceModal.svelte` | 438 | C3 | Find & replace |
| `CorrectionCell.svelte` | 343 | C3 | Inline edit cell |
| `RunTabContent.svelte` | 342 | C3 | Run tab content [NEW] |
| `TermCorrectionPopup.svelte` | 345 | C3 | Correction popup |
| `TargetSchemaHint.svelte` | 316 | C2 | Target schema hint [NEW] |
| `ScheduleConfig.svelte` | 300 | C3 | Schedule editor |
| `BulkCorrectionSidebar.svelte` | 288 | C3 | Bulk correction |
| `TranslationRunProgress.svelte` | 277 | C4 | Live progress |
| `TargetTabForm.svelte` | 224 | C3 | Target tab form [NEW] |
| `TranslationRunGlobalIndicator.svelte` | 237 | C2 | Global run indicator |
| `TranslationMetricsDashboard.svelte` | 186 | C2 | Metrics dashboard |
#### Frontend страницы (5 файлов, 1 237 строк)
| Файл | Строк | Назначение |
|------|:-----:|------------|
| `translate/+page.svelte` | 288 | Job list |
| `translate/[id]/+page.svelte` | 269 | Job config + run |
| `translate/history/+page.svelte` | 231 | Run history |
| `translate/dictionaries/+page.svelte` | 247 | Dictionary list |
| `translate/dictionaries/[id]/+page.svelte` | 202 | Dictionary editor |
### Phase 11 Completion Details
- **Migration (T083-T086)**: Models, schemas, Alembic — done
- **US6 Auto-detection (T087-T090)**: LLM returns BCP-47, tests added
- **US7 Multilingual dict (T091-T095)**: Language-pair aware CRUD + filtering
- **US1/2/3 Multi-language (T096-T108)**: Config → Preview → Execution
- **US8 Inline correction (T109-T118)**: Endpoints, BulkReplaceModal, CorrectionCell
- **US8b Context-aware (T123-T134)**: context_data, ContextAwarePromptBuilder, Jaccard
- **US4 History (T119-T122)**: Per-language stats, MetricSnapshot
### Расхождения с планом (документировано post-factum)
| Аспект | План | Реальность |
|--------|------|------------|
| Архитектура | Plugin-based | Service-based (plugin.py — скелет C2) |
| Routes | Один translate.py | Пакет из 13 файлов (2 296 строк) |
| Plugin source | ~15 файлов | 59 файлов (полная декомпозиция: orchestrator 14, executor 9, preview 11, dictionary 7, service 6, standalone 12) |
| SQLGenerator | C4 | C3 (pure function, нет I/O) |
| TranslateRoutes | C3 | C4 (пакет 13 файлов, 6+ зависимостей) |
| Новые модули (post-05-17) | — | _lang_detect.py, _text_cleaner.py, _llm_async_http.py, service_target_schema.py |
| Новые компоненты (post-05-17) | — | ConfigTabForm, RunTabContent, TargetTabForm, TargetSchemaHint |
| Frontend pages | ~3 256 строк | 1 237 строк (рефакторинг) |
### Verified
- Backend: ~580 pytest ✅
- Frontend: ~68 vitest ✅
- Browser: validated — preview, correction, dictionary flows work
- Enhancement verified: DbExecutor, ConnectionService, InsertMethodSelector ✅
- Semantic audit: 0 warnings across all checks
### Remaining
- None. All 160 tasks are [x].
### Next Action
- `ready_for_review` — Spec 028 is complete, ready for merge to main