# Дизайн: параметрика (пакет D) — переменные модели

**Дата:** 2026-05-27
**Статус:** реализовано и проверено (тест зелёный); на ревью (ревьюер — Codex)

## Цель

Дать запись параметрики: **создание, изменение и удаление переменных модели** (`list_variables`
уже читает). Это фундамент управляемых моделей: пользовательские переменные, формулы со ссылками,
внешние переменные для сборок.

## Сигнатуры (рефлексия interop + справка)

```
ksVariableCollection:
    object AddNewVariable(string name, double value, string note)   // создать (ТОЛЬКО на ksFeature!)
    object GetByName(string name, bool testFullName, bool testIgnoreCase)
    bool   RemoveVariable(string name)
    int    GetCount(); object GetByIndex(int)
ksVariable:
    string name; string Expression; double value; bool external; bool Information; string note
ksPart:    bool RebuildModel()          // применить изменения переменных
ksFeature: object VariableCollection    // СВОЙСТВО (не метод), все переменные детали
```

## Ключевые факты (исследование + эмпирика spike)

1. **Создавать переменные можно только на коллекции корневого `ksFeature`**
   (`ksPart.GetFeature().VariableCollection`), НЕ на `ksPart.VariableCollection()` (та — только
   внешние). `AddNewVariable` на ksPart-коллекции не создаёт пользовательскую переменную.
2. **`Expression` — ведущее поле**, `value` — вычисленный результат. Менять значение надо через
   `Expression` (константа «30» или формула «width*2»); прямое `value=` ненадёжно (перезапишется).
3. **Изменения вступают в силу после `ksPart.RebuildModel()`**; `value` пересчитывается из
   `Expression`. RCW переменной после RebuildModel «застревает» на старом значении — **перечитывать**
   переменную из свежей коллекции (`ReadValueFresh`).
4. **`list_variables` исправлен**: читал `ksPart.VariableCollection()` (только внешние) → теперь
   `ksPart.GetFeature().VariableCollection` (все, включая созданные), с фолбэком. Иначе созданная
   переменная не была бы видна агенту.
5. **Удаление**: нельзя удалить переменную, на которую ссылаются другие (RRemoveVariable→FALSE);
   удалять в порядке зависимостей.
6. **ОГРАНИЧЕНИЕ (важно для контракта):** переменная управляет геометрией только в
   **параметрической** модели (размеры эскизов связаны с именами переменных). Наши эскизы строятся
   литеральными координатами — у них нет именованных размеров, поэтому set_variable хранит/считает
   значение, но геометрию не меняет. Привязка размеров эскиза к переменным (параметрические эскизы)
   — отдельный большой пласт (будущее). Поэтому тест проверяет CRUD/вычисление через чтение, а не
   изменение геометрии.

## MCP-инструменты (новая группа, `VariableTools`)

| Инструмент | Параметры | Поведение |
|---|---|---|
| `create_variable` | `name`, `value`, `note?`, `external=false` | Создать переменную. Возвращает значение. |
| `set_variable` | `name`, `expression` | Задать выражение (константа/формула), перестроить, вернуть значение. |
| `delete_variable` | `name` | Удалить (нельзя, если есть зависимые). |

Чтение — существующий `list_variables` (теперь видит все переменные).

## Реализация

- **`src/Kompas.Mcp.Core/Modeling/VariableService.cs`** (новый): `CreateVariableAsync`,
  `SetVariableAsync`, `DeleteVariableAsync`, `GetVariableValueAsync` (read-back) на STA-потоке.
  Хелпер `RootVariables()` → (`ksPart`, feature-коллекция); `ReadValueFresh(name)` после RebuildModel.
  Валидация: имя не пусто, value конечно, выражение не пусто.
- **`VariableTools.cs`** (новый): 3 инструмента; DI `AddSingleton<VariableService>()`.
- **`ModelInspectionService.ReadVariables`**: источник → feature-коллекция (фолбэк на ksPart).
- Транзитные RCW не освобождаем точечно — консистентно (долг v2-2).

## Тестирование (Integration, `VariableTests`)

CRUD round-trip: построить коробку (дерево построения) → `create_variable("width",40)` (значение 40;
видна в `list_variables`) → `set_variable("width","12+8")` → 20 → создать `height`, `set "width*2"` →
40 (формула со ссылкой) → удалить `height`, затем `width` (порядок зависимостей) → обе исчезли
(`GetVariableValueAsync` бросает `KeyNotFoundException`). ✓
