## Tool Surface у Tauri (реалізація ядра `tool-surface`)

Це per-stack реалізація правила **`tool-surface`** (`n-tool-surface`) для Tauri+Rust. Контракт (каталог → `dispatch` → UI/оркестратор/LLM, інваріант паритету, конверт) — у тому правилі; тут — як він лягає на Tauri.

**Реальна робота живе в Rust, JS-каталог — тонкий call surface.** Handler тула не містить логіки сам — він **делегує** в native. Одна реалізація в Rust-крейті backs два споживачі:

- **Бінарник** (`src-tauri` як CLI, або окремий crate-bin) — headless-вхід для оркестратора;
- **Tauri-команда** (`#[tauri::command]`) — той самий крейт-fn, обгорнутий для UI.

**Два транспорти одного каталогу** (`src/tool/transports`):

- **UI (in-app):** `invoke(tool.tauri, input)` → Tauri-команда → крейт. Ключі `input` мапляться 1:1 на аргументи команди (camelCase, напр. `tasksDir`); поля вкладених struct лишаються snake_case (Tauri конвертить лише імена top-level аргументів).
- **Оркестратор (headless):** `bin/<app>.mjs` спавнить зібраний бінарник (`<bin> <verb> …` per-verb, або уніфікований `<bin> exec '<json>'`), парсить JSON stdout.

**Конверт:** Tauri-команда повертає `Result<T, String>`; адаптер мапить у `{ ok, output }` / `{ ok:false, error }`. Бінарник друкує конверт у stdout, exit ≠ 0 на `ok:false`.

**Єдине джерело схем:** надавай перевагу `schemars`-derive на Rust-param-структурах → бінарник віддає маніфест (`<bin> schema`); або тримай схему в JS-каталозі й валідуй до `invoke`. **Не дублюй** контракт між Rust і JS — лише деривація + спільні тест-вектори.

**LLM-раннер in-app:** chat-loop ходить до OpenAI-сумісного ендпоінта через `tauri-plugin-http` fetch (бо webview-fetch обмежений CSP/capability), а тули виконує через спільний `dispatch` (той самий, що й UI).

### Що перевіряє автоматичний lint (`main.mjs`)

Для кожного Tauri-workspace (маркер — `tauri.conf.json` чи `src-tauri/tauri.conf.json`) детектор збирає slugs плагінів, яких торкається JS/TS/Vue-код у `src/**`: через wrapper-import `@tauri-apps/plugin-<slug>` або прямий `invoke('plugin:<slug>|<command>')`. Для кожного знайденого `<slug>` перевіряються три незалежні умови — будь-яка провалена дає окрему violation:

1. **`tool-surface-plugin-dep-missing`** — `src-tauri/Cargo.toml` не оголошує crate `tauri-plugin-<slug>` у жодній секції `[dependencies]`/`[target.*.dependencies]`.
2. **`tool-surface-plugin-not-registered`** — `src-tauri/src/lib.rs` ніде не згадує rust-ідентифікатор плагіна (`tauri-plugin-dialog` → `tauri_plugin_dialog`, дефіси заміняються на підкреслення), тобто `builder` його не реєструє.
3. **`tool-surface-plugin-capability-missing`** — жоден файл `src-tauri/capabilities/*.json` не дає permission `<slug>` чи `<slug>:*`.

Кожна з трьох дір — тихий рантайм-фейл без компіляційної помилки: (1) код не збереться на CI іншого профілю або впаде при оновленні залежностей, (2) `invoke` впаде з "plugin not found", (3) `invoke` впаде permission-denied, видимий лише в `console.error`. Перевірено вживу на проєкті myllm.
