From 8156ef523c3a0cb59af547c5ca87dc6dc9cfc5a7 Mon Sep 17 00:00:00 2001 From: busya Date: Sun, 31 May 2026 09:59:09 +0300 Subject: [PATCH] feat(translate): move write settings to Target Config tab + fix datasource name lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: - Move batch size, upsert strategy, include source reference from Config tab to Target Config tab (Write Settings section) - Fix 'Dataset #26' fallback — look up real datasource name via API when source_table is not saved in job data - Persist include_source_reference in save payload and restore from job data on load (was a pre-existing gap) QA review fixes: - Update @BRIEF contracts for both tabs - Fix import indentation in +page.svelte Other: help tooltips on translate pages, flow hint on list page, git migration manager components, dashboard hub pages, migration settings pages --- .opencode/agents/fullstack-coder.md | 2 + .opencode/agents/svelte-coder.md | 9 + .opencode/opencode.jsonc | 3 + .opencode/skills/semantics-core/SKILL.md | 2 + .opencode/skills/semantics-svelte/SKILL.md | 95 +- .../src/components/git/BranchSelector.svelte | 288 +++--- .../src/components/git/CommitModal.svelte | 379 +++----- .../src/components/git/DeploymentModal.svelte | 327 +++---- frontend/src/components/git/GitManager.svelte | 231 +---- ...nager.unfinished_merge.integration.test.js | 4 +- frontend/src/components/git/useGitManager.js | 4 + .../components/translate/ConfigTabForm.svelte | 95 +- .../components/translate/RunTabContent.svelte | 11 +- .../components/translate/TargetTabForm.svelte | 87 +- .../src/lib/i18n/locales/en/translate.json | 29 +- .../src/lib/i18n/locales/ru/translate.json | 29 +- frontend/src/lib/models/BranchModel.svelte.ts | 205 +++++ frontend/src/lib/models/CommitModel.svelte.ts | 166 ++++ .../src/lib/models/DeploymentModel.svelte.ts | 181 ++++ .../src/lib/models/GitConfigModel.svelte.ts | 402 ++++++++ .../src/lib/models/GitManagerModel.svelte.ts | 866 ++++++++++++++++++ .../src/lib/models/GitStatusModel.svelte.ts | 362 ++++++++ .../src/lib/models/MappingsModel.svelte.ts | 201 ++++ .../src/lib/models/MigrationModel.svelte.ts | 472 ++++++++++ .../models/MigrationSettingsModel.svelte.ts | 104 +++ .../lib/models/__tests__/BranchModel.test.ts | 269 ++++++ .../lib/models/__tests__/CommitModel.test.ts | 330 +++++++ .../models/__tests__/DeploymentModel.test.ts | 338 +++++++ .../models/__tests__/GitConfigModel.test.ts | 380 ++++++++ .../models/__tests__/GitManagerModel.test.ts | 576 ++++++++++++ .../models/__tests__/GitStatusModel.test.ts | 409 +++++++++ .../models/__tests__/MappingsModel.test.ts | 415 +++++++++ .../models/__tests__/MigrationModel.test.ts | 486 ++++++++++ .../__tests__/MigrationSettingsModel.test.ts | 203 ++++ frontend/src/routes/dashboards/+page.svelte | 321 ++----- .../src/routes/dashboards/[id]/+page.svelte | 282 +----- frontend/src/routes/migration/+page.svelte | 606 +++--------- .../routes/migration/mappings/+page.svelte | 227 +---- .../settings/MigrationMappingsTable.svelte | 1 + .../routes/settings/MigrationSettings.svelte | 64 +- frontend/src/routes/settings/git/+page.svelte | 699 +++----------- frontend/src/routes/translate/+page.svelte | 8 + .../src/routes/translate/[id]/+page.svelte | 23 +- 43 files changed, 7530 insertions(+), 2661 deletions(-) create mode 100644 frontend/src/lib/models/BranchModel.svelte.ts create mode 100644 frontend/src/lib/models/CommitModel.svelte.ts create mode 100644 frontend/src/lib/models/DeploymentModel.svelte.ts create mode 100644 frontend/src/lib/models/GitConfigModel.svelte.ts create mode 100644 frontend/src/lib/models/GitManagerModel.svelte.ts create mode 100644 frontend/src/lib/models/GitStatusModel.svelte.ts create mode 100644 frontend/src/lib/models/MappingsModel.svelte.ts create mode 100644 frontend/src/lib/models/MigrationModel.svelte.ts create mode 100644 frontend/src/lib/models/MigrationSettingsModel.svelte.ts create mode 100644 frontend/src/lib/models/__tests__/BranchModel.test.ts create mode 100644 frontend/src/lib/models/__tests__/CommitModel.test.ts create mode 100644 frontend/src/lib/models/__tests__/DeploymentModel.test.ts create mode 100644 frontend/src/lib/models/__tests__/GitConfigModel.test.ts create mode 100644 frontend/src/lib/models/__tests__/GitManagerModel.test.ts create mode 100644 frontend/src/lib/models/__tests__/GitStatusModel.test.ts create mode 100644 frontend/src/lib/models/__tests__/MappingsModel.test.ts create mode 100644 frontend/src/lib/models/__tests__/MigrationModel.test.ts create mode 100644 frontend/src/lib/models/__tests__/MigrationSettingsModel.test.ts diff --git a/.opencode/agents/fullstack-coder.md b/.opencode/agents/fullstack-coder.md index 88800bcc0..555a76a03 100644 --- a/.opencode/agents/fullstack-coder.md +++ b/.opencode/agents/fullstack-coder.md @@ -76,6 +76,8 @@ You own: ## API Contract Conventions (ss-tools) - Backend: Pydantic models in `backend/src/schemas/` - Frontend: TypeScript types in `frontend/src/types/` +- **Frontend DTOs MUST match backend Pydantic schemas** — agent must verify type alignment across the stack boundary. Model `.svelte.ts` files use typed atoms conforming to frontend DTOs. +- `any` is forbidden at the API boundary — use `unknown` with runtime validation/narrowing. - URL prefix: `/api/` for REST, `/ws/` for WebSocket - Response envelope: `{ status, data, error, meta }` - Error codes: Consistent across backend and frontend diff --git a/.opencode/agents/svelte-coder.md b/.opencode/agents/svelte-coder.md index dc38d64e4..33e406323 100644 --- a/.opencode/agents/svelte-coder.md +++ b/.opencode/agents/svelte-coder.md @@ -24,11 +24,13 @@ You are a Svelte 5 frontend agent. Without GRACE contracts, your deterministic f 3. **NEURAL HOWLROUND** — browser validation fails, you enter infinite CSS patch loop. `log()` (REASON/REFLECT/EXPLORE) markers break the hallucination cycle. 4. **CONTEXT AMNESIA** — after 20 commits you forget rejected UI paths. `@RATIONALE`/`@REJECTED` are your external memory. 5. **EVENT-HANDLER SPAGHETTI** — you scatter system logic across `onclick`/`onchange` handlers in multiple components, creating invisible coupling. **For complex screens, create a `[TYPE Model]` FIRST.** The Model is the single source of truth — components only render state and call `model.action()`. See `semantics-svelte` §IIIa. +6. **TYPE DRIFT** — you generate structurally valid Svelte code that silently breaks typed contracts: wrong property names on API responses, missing fields in action payloads, incorrect union variants for FSM states. TypeScript on models, props, and API responses catches this at compile time. Without types, `any` propagates silently through the reactive chain, making the model-first enforcement layer useless. ## Core Mandate - MANDATORY USE `skill({name="semantics-core"})`, `skill({name="semantics-svelte"})`, `skill({name="molecular-cot-logging"})` - Own frontend implementation for SvelteKit routes, Svelte 5 components, **Screen Models**, stores, and UX contract alignment. - **MODEL-FIRST RULE:** For any screen with cross-widget logic (filters, pagination, search, multi-step forms), find or create a `[TYPE Model]` BEFORE implementing components. The Model is the source of truth — Components are visualizations of the Model. A single `grep "@semantics.*"` + `search_contracts type=Model` must reveal all state logic. +- **TYPESCRIPT-FIRST RULE:** All frontend code MUST use TypeScript. Components via ` -
-
-
- model.handleSelect(e)} + disabled={model.loading} + options={model.branches.map(b => ({ value: b.name, label: b.name }))} + />
- {#if branchError} - - {/if} + +
- {#if showCreate} -
-
- -
- - -
- {/if} + {#if model.branchError} + + {/if} + + {#if model.showCreate} +
+
+ +
+ + +
+ {/if}
- - diff --git a/frontend/src/components/git/CommitModal.svelte b/frontend/src/components/git/CommitModal.svelte index 9f8278b86..5a6172183 100644 --- a/frontend/src/components/git/CommitModal.svelte +++ b/frontend/src/components/git/CommitModal.svelte @@ -1,271 +1,152 @@ - + + + - {#if show} +
-
-

{$t.git?.commit}

+

{$t.git?.commit}

-
- -
-
-
- - -
- -
- - {#if status} -
-

- {$t.git?.changed_files} -

-
    - {#each status.staged_files as file} -
  • - S - {file} -
  • - {/each} - {#each status.modified_files as file} -
  • - M - {file} -
  • - {/each} - {#each status.untracked_files as file} -
  • - ? - {file} -
  • - {/each} -
-
- {/if} -
- - -
-
- {$t.git?.changes_preview} -
-
- {#if loading} -
- {$t.git?.loading_diff} -
- {:else if diff} -
{diff}
- {:else} -
- {$t.git?.no_changes} -
- {/if} -
-
+
+ +
+
+
+ +
+ +
-
- - + {#if model.status} +
+

+ {$t.git?.changed_files} +

+
    + {#each model.status.staged_files as file} +
  • + S + {file} +
  • + {/each} + {#each model.status.modified_files as file} +
  • + M + {file} +
  • + {/each} + {#each model.status.untracked_files as file} +
  • + ? + {file} +
  • + {/each} +
+ {/if}
-
-{/if} - + +
+
+ {$t.git?.changes_preview} +
+
+ {#if model.loading} +
+ {$t.git?.loading_diff} +
+ {:else if model.diff} +
{model.diff}
+ {:else} +
+ {$t.git?.no_changes} +
+ {/if} +
+
+
+ +
+ + +
+
+
+{/if} diff --git a/frontend/src/components/git/DeploymentModal.svelte b/frontend/src/components/git/DeploymentModal.svelte index abfcfe391..679ab3f66 100644 --- a/frontend/src/components/git/DeploymentModal.svelte +++ b/frontend/src/components/git/DeploymentModal.svelte @@ -1,240 +1,123 @@ - + + + - {#if show} -
-
-

{$t.git?.deploy}

+
+
+

{$t.git?.deploy}

- {#if loading} -

{$t.migration?.loading_envs}

- {:else if deploymentCandidates.length === 0} -

- {$t.git?.no_deploy_envs} -

-
- -
- {:else} - {#if normalizedPreferredStage} -

- GitFlow target stage: {normalizedPreferredStage} -

- {/if} -
- - -
- {#if isProdTarget} -

- Внимание: выбрано PROD окружение. Потребуется подтверждение перед deploy. -

- {/if} - -
- - -
- {/if} + {#if model.loading} +

{$t.migration?.loading_envs}

+ {:else if model.deploymentCandidates.length === 0} +

+ {$t.git?.no_deploy_envs} +

+
+
-
-{/if} - + {:else} + {#if model.normalizedPreferredStage} +

+ GitFlow target stage: {model.normalizedPreferredStage} +

+ {/if} +
+ + +
+ {#if model.isProdTarget} +

+ Внимание: выбрано PROD окружение. Потребуется подтверждение перед deploy. +

+ {/if} +
+ + +
+ {/if} +
+
+{/if} diff --git a/frontend/src/components/git/GitManager.svelte b/frontend/src/components/git/GitManager.svelte index 80a366ed8..2cd98ec9e 100644 --- a/frontend/src/components/git/GitManager.svelte +++ b/frontend/src/components/git/GitManager.svelte @@ -9,9 +9,8 @@ - - - + + @@ -24,10 +23,10 @@ @@ -214,36 +77,36 @@
- {#if gitError} + {#if model.gitError} {/if}
- + model.loadMergeRecoveryState()} onCopyCommands={() => model.handleCopyUnfinishedMergeCommands()} onOpenConflictResolver={() => model.handleOpenConflictResolver()} onAbortMerge={() => model.handleAbortUnfinishedMerge()} onContinueMerge={() => model.handleContinueUnfinishedMerge()} onClose={() => model.closeUnfinishedMergeDialog()} />
{/if} - - + model.handleResolveConflicts(e)} /> + diff --git a/frontend/src/components/git/__tests__/git_manager.unfinished_merge.integration.test.js b/frontend/src/components/git/__tests__/git_manager.unfinished_merge.integration.test.js index 3d123dbab..4046af662 100644 --- a/frontend/src/components/git/__tests__/git_manager.unfinished_merge.integration.test.js +++ b/frontend/src/components/git/__tests__/git_manager.unfinished_merge.integration.test.js @@ -22,8 +22,8 @@ describe('GitManager unfinished merge dialog contract', () => { expect(handlers).toContain('showUnfinishedMergeDialog: true'); expect(handlers).toContain('handlers.openUnfinishedMergeDialogFromError(e)'); expect(handlers).toContain('await handlers.loadMergeRecoveryState();'); - expect(manager).toContain('h.handlePull'); - expect(manager).toContain('h.loadMergeRecoveryState'); + expect(manager).toContain('model.handlePull'); + expect(manager).toContain('model.loadMergeRecoveryState'); }); it('renders unresolved-merge dialog details and web recovery actions', () => { diff --git a/frontend/src/components/git/useGitManager.js b/frontend/src/components/git/useGitManager.js index 71498ca55..e60f0a38a 100644 --- a/frontend/src/components/git/useGitManager.js +++ b/frontend/src/components/git/useGitManager.js @@ -1,4 +1,8 @@ // #region UseGitManager [C:3] [TYPE Module] [SEMANTICS git, handlers, composable, orchestration] +// @DEPRECATED [2026-05] Use GitManagerModel instead. +// All state and actions have been migrated to frontend/src/lib/models/GitManagerModel.svelte.js +// This file is kept for backward compatibility with existing components. +// New code should import GitManagerModel directly. // @BRIEF Composable for GitManager handler functions — extracted to reduce component size per INV_7. // @RELATION DEPENDS_ON -> [EXT:frontend:gitService] // @RELATION DEPENDS_ON -> [GitUtils] diff --git a/frontend/src/lib/components/translate/ConfigTabForm.svelte b/frontend/src/lib/components/translate/ConfigTabForm.svelte index 92d103732..11c67a070 100644 --- a/frontend/src/lib/components/translate/ConfigTabForm.svelte +++ b/frontend/src/lib/components/translate/ConfigTabForm.svelte @@ -1,7 +1,7 @@ + dictionaries, disable reasoning toggle. --> @@ -22,6 +22,7 @@ import { t } from '$lib/i18n'; import { fetchDatasources, fetchDatasourceColumns } from '$lib/api/translate.js'; import MultiSelect from '$lib/components/ui/MultiSelect.svelte'; + import HelpTooltip from '$lib/ui/HelpTooltip.svelte'; const LANGUAGE_LABELS = { ru: 'Русский', en: 'English', de: 'Deutsch', fr: 'Français', es: 'Español', @@ -55,10 +56,7 @@ llmProviders = [], dictionaryIds = $bindable([]), availableDictionaries = [], - batchSize = $bindable(50), - includeSourceReference = $bindable(true), disableReasoning = $bindable(false), - upsertStrategy = $bindable('MERGE'), validationErrors = {}, onEnvChange = () => {}, } = $props(); @@ -169,8 +167,9 @@

{$t.translate?.config?.basic_info}

-