diff --git a/prompt_for_opus_modifier_architecture_full.md b/prompt_for_opus_modifier_architecture_full.md new file mode 100644 index 0000000..d1271da --- /dev/null +++ b/prompt_for_opus_modifier_architecture_full.md @@ -0,0 +1,73 @@ +# Спроектировать С НУЛЯ архитектуру/логику «ресурсов-модификаторов» (kind: modifier) + +## Цель + +Перепроектировать модификаторы целиком, чтобы исключить ВСЕ классы багов, не латать по одному. +Нужна единая, полная модель поведения — без догадок и костылей. Перечислить ВСЕ кейсы. + +## Что такое модификатор (текущая фактура) + +В YAML (генерируется из API) операции бывают: +- `kind: instance` (create/modify/suspend/resume/delete) — обычный CRUD-ресурс; +- `kind: modifier` + `modifier: ` — отдельный TF-ресурс, который вызывает `modify` + на родительском инстансе. Сейчас их два: `vc_org.ip_space`, `vc_nsxt.network`. + +Реальные примеры: +- `vc_org` → modifier `ip_space` (modify 207), параметр `vIPConfigure` (array-map-fixed); +- `vc_nsxt` → modifier `network` (modify 111), параметры `needEnableAVI`(bool), + `virtualServicesCount`(int>0), `qosProfile`(string), `ipSpaceName`(string), + `routedNetConfiguration`(map-fixed). + +## Текущий механизм (что есть — факты, не догадки) + +1. Генератор: `TOOLS/resource-generator/internal/templates/modifier.go` + - Create и Update **идентичны**: оба шлют `modify` с полным набором полей. + - `Delete` — **no-op** (комментарий: «no confirmed inverse payload»). + - Схема: `id` computed, `_id` required, поля Optional (или Required если нет default). +2. `resources_core.CompactParams` — выбрасывает пустые строки из payload. +3. `resources_core.BuildActionID(instanceUID, operation, modifierName)` — константный ID, + не привязан к реальной операции (opUid не сохраняется). +4. `core.RunInstanceOperationUniversalByCode` — резолвит code→id через + `GET /instanceOperations/{opUid}?fields=cfsParams` (fallback на `/default/{opId}`); + отправляет переданные params, затем дозаполняет остальные их live-значением + (guard: пропускает параметр, если нет ни ParamValue, ни DefaultValue). +5. `Read` — через `RefreshResourceState`: читает `state_params` инстанса и + перезаписывает input-поля из них. + +## Уже выявленные КЛАССЫ багов (все реально случились) + +- **A. Сброс create-поля при modify.** modify со сброшенными (null) параметрами + трактуется бэкендом как reset-to-default: `needEnableAVI` стал false после + create=true. Причина: модификатор шлёт только свои поля, `CompactParams` выкидывает + пустые, бэкенд видит «отсутствующий» и сбрасывает. +- **B. Досылка синтетики.** фикс «досылать всё» слал `"0"` для `integer > 0` + (параметр `virtualServicesCount`), API 400 «Invalid format integer > 0». +- **C. Ложное «Нельзя изменить».** `ComputeCreateOnly` считал `needEnableAVI` + CreateOnly (change-forbidden), хотя в YAML `is_modifiable: true` — потому что + генератор не учитывал modifier-канал и терял `IsModifiable`. (Зафиксировано отдельно.) +- **D. No-op Delete оставляет эффект на платформе.** destroy модификатора убирает + ресурс из state, но выделенные IP / включённый ALB остаются на платформе → drift. +- **E. Повторный apply после taint/replace** снова гонит modify — риск повторной + аллокации (для `ip_space`), идемпотентность не гарантирована. + +## Вопросы к Опусу (ответить ПОЛНО, по пунктам, с точными местами правки) + +1. **Канон «как сравнить и применить».** Должен ли модификатор перед modify + читать текущее состояние и слать ДЕЛЬТУ (только реально изменившиеся поля), + или ПТЦ полный payload? Как детектить drift в Read? +2. **Досылка незаданных полей (паер-заливы A и B).** Какое каноническое правило: + когда досылать live-значение, когда дефолт, когда пропускать? Как не сломать + `integer > 0` и прочие constraints? +3. **Delete/rollback.** Где искать обратный payload? Как правильно поступить, пока + обратный payload НЕ подтверждён API (no-op допустим? явная ошибка? suspend?). +4. **Idempotency + ID.** Как сделать ID модификатора отражающим фактическую операцию + (opUid?) и как предотвратить двойную аллокацию при replace/повторном apply? +5. **Связь с родителем.** Должен ли модификатор использовать `_id` как ссылку + на родителя (depends_on / borrow state), и как читать UUID родителя? +6. **Create vs Update.** Допустимо ли иметь их идентичными, или нужен строго Update-семантик + (нет create, только apply-по-десяти)? +7. **Полный перечень кейсов.** Перечислить ВСЕ edge-кейсы, которые надо покрыть: + create родителя → modifier; remove modifier; replace; partial params; unknown/absent. + +Ответ — архитектурный документ (краткий, структурированный), с конкретными файлами +и функциями. НЕ код-ревью, а ПРОЕКТ.