n7n-plugin-host 0.1.5

wasmtime-бекенд для crate::registry::PluginRegistry (n7n-harness) — друга (wasm-компонентна) реалізація слотів detector/verify/ast_facts/t0, поруч з in-process-бекендом §3.12 пункту 16а
Documentation
package n7n:fix-deps@0.1.0;

// Спільний словник для чотирьох поверхонь `FixDeps`, які WIT/wasm робить
// компонентами (спека `docs/specs/2026-08-17-n7n-harness-local-models.md`,
// §2 рішення И, §3.9, §3.12 пункт 16, перша половина: контракти й хостовий
// реєстр — БЕЗ wasmtime). Типи тут дзеркалять УЖЕ наявні Rust-типи один в
// один, а не вводять нове поняття:
//
//   record violation      ← harness::pipeline::Violation
//   record verify-report  ← llm_lib::attempt::VerifyReport
//   record edit-plan (+   ← llm_lib::edit_plan::{EditPlan, FileEditPlan,
//     anchored-edit,        AnchoredEdit}
//     anchored-file-edit,
//     create-file-edit,
//     delete-file-edit)
//   type rung-id = string ← llm_lib::journal::RungId
//
// Ці файли — ЖИВИЙ контракт: `crates/plugin-host` парсить їх через
// `wasmtime::component::bindgen!` і виконує реальні wasm-компоненти проти
// них (п.16б; історично 16а писав їх без інструментів — той стан минув).
// Тобто синтаксична помилка тут ламає збірку `plugin-host`, а розходження
// форми з Rust-типами ловлять два місця: bindgen!-конвертери в
// `plugin-host` і компіляційний тест `wit_conformance` у
// `crates/harness/src/registry.rs` (дзеркальні структури, kebab-case полів
// буквально copy-paste з `record`-ів нижче).

/// Спільні типи чотирьох поверхонь `FixDeps` (`detector`, `verify`,
/// `ast-facts`, `t0`).
interface types {
    /// Одне порушення від детектора — дзеркало
    /// `harness::pipeline::Violation`.
    ///
    /// `line` — `u32`, не Rust-івський `usize`: WIT не має
    /// платформозалежної цілочисельної ширини, лише фіксовані розміри.
    /// `u32` покриває будь-який реальний файл (понад 4 млрд рядків — інша
    /// проблема), звуження ширини не губить жодного реального значення.
    record violation {
        /// Файл із порушенням, відносно `cwd` спроби (або абсолютний).
        ///
        /// WIT `string` — валідний UTF-8 за визначенням component model
        /// (https://webassembly.github.io/component-model/). Rust-сторона
        /// несе `PathBuf`, який на деяких платформах теоретично може
        /// містити не-UTF-8 байти — `cwd` тут завжди git-репозиторій
        /// користувача (текстові джерела лінт-поверхні), тож розбіжність
        /// приймається свідомо, не приховується (звіт задачі, розділ "що
        /// не виразилось у WIT").
        file: string,
        /// Рядок порушення, якщо детектор його дає.
        line: option<u32>,
        /// Текст повідомлення детектора.
        message: string,
    }

    /// Результат канонічної перевірки — дзеркало
    /// `llm_lib::attempt::VerifyReport`.
    record verify-report {
        /// Перевірка зелена — порушення закрите.
        ok: bool,
        /// Точний вивід перевірки — подається моделі фідбеком при
        /// `ok == false`.
        output: string,
        /// Помилка САМОЇ перевірки (інфраструктурна проблема, не "червоний"
        /// вердикт) — не палить бюджет verify-петлі (§3.8 спеки).
        infra-error: bool,
    }

    /// Одна анкерна правка рядка — дзеркало
    /// `llm_lib::edit_plan::AnchoredEdit`.
    record anchored-edit {
        /// 3-символьний base36-якір, з яким рядок мав збігатися.
        anchor: string,
        /// 1-based номер рядка в поточному файлі (щодо `u32` — доккоментар
        /// `violation.line` вище).
        line: u32,
        /// Заміна вмісту рядка; відсутнє значення = видалити рядок
        /// (Rust `None`, той самий сентинел, що серіалізує в JSON `null`).
        new-text: option<string>,
    }

    /// Anchored-правки одного файлу — варіант `FileEditPlan::Anchored`.
    record anchored-file-edit {
        path: string,
        edits: list<anchored-edit>,
    }

    /// Створення нового файлу цілком — варіант `FileEditPlan::Create`.
    /// Файл, що вже існує, — помилка застосування на боці хоста
    /// (`write_guard`), не мовчазний перезапис (доккоментар
    /// `edit_plan.rs`: "повний перезапис наявного файлу свідомо
    /// невиразимий").
    record create-file-edit {
        path: string,
        content: string,
    }

    /// Видалення наявного файлу цілком — варіант `FileEditPlan::Delete`.
    record delete-file-edit {
        path: string,
    }

    /// План правок одного файлу — дзеркало
    /// `llm_lib::edit_plan::FileEditPlan`. Rust-сторона кодує закритий
    /// вибір через `#[serde(tag = "kind")]`; WIT висловлює той самий вибір
    /// НАТИВНО через `variant`, без окремого тегового поля.
    variant file-edit-plan {
        anchored(anchored-file-edit),
        create(create-file-edit),
        delete(delete-file-edit),
    }

    /// Декларативний план правок T0 — дзеркало
    /// `llm_lib::edit_plan::EditPlan` (§3.9 спеки: "T0 повертає
    /// декларативний план, не файлову систему"). Порожній `files` —
    /// валідний сигнал "нічого не робимо", той самий сентинел, що
    /// `EditPlan::empty()`/`EditPlan::is_empty()`.
    record edit-plan {
        files: list<file-edit-plan>,
    }

    /// Ідентифікатор рунга — дзеркало `llm_lib::journal::RungId`.
    ///
    /// Rust-сторона — напівзакритий тип: п'ять іменованих констант
    /// (`LOCAL`/`LOCAL_RETRY`/`CLOUD_MIN`/`CLOUD_AVG`/`CLOUD_MAX`) плюс
    /// відкритий `RungId::other(String)` escape-hatch
    /// (`Cow<'static, str>` усередині, `#[serde(transparent)]` — на дроті
    /// це завжди просто рядок). WIT не має ідіоми "variant із
    /// фіксованими відомими значеннями + відкритий рядок-catch-all":
    /// `variant` вимагав би вичерпного переліку case-ів, а `other`-гілка
    /// тоді дублювала б рівно те, що робить рядковий `type`. Тому — простий
    /// psevdonim `type rung-id = string` (`RungId::as_str()`), той самий
    /// підхід, що журнал уже застосовує на власному Rust-боці: `as_str()`
    /// для серіалізації/телеметрії, порівняння лишається на типізованому
    /// `RungId`, не на голому рядку.
    ///
    /// **Жодна з чотирьох функцій (`detector.detect`, `verify.run`,
    /// `ast-facts.facts`, `t0.plan`) не приймає `rung-id` як параметр
    /// сьогодні.** `RungId` живе на межі attempt/журналу
    /// (`llm_lib::attempt::FixRequest::rung`, `journal::Event.rung`), а не
    /// в сигнатурах цих чотирьох injectable-залежностей: детектор і T0 —
    /// concern-рівневі (`journal::Event.rung == None` для T0-подій,
    /// доккоментар `journal.rs`: "події T0 йдуть у журнал до старту сесії
    /// моделі"), не рунгові. Тип включений у спільний словник за прямою
    /// вимогою задачі й на випадок, якщо майбутня rung-scoped поверхня
    /// (напр. `verify`, який на Rust-боці вже сьогодні рунговий через
    /// `FixRequest::rung`) захоче нести його явно як параметр — це
    /// розширення сигнатури `verify.run`, яке ЦЕЙ крок навмисно не робить
    /// (WIT відображає наявні сигнатури, не вигадує нові).
    type rung-id = string;
}