fix(core): idempotency pre-check без схемы — единый источник (live)
Код-ревью (раунд 5), п.1/6: pre-check брал схему из GET /instanceOperations/default/{opId},
а payload строился по живой схеме ?fields=cfsParams — два источника. default может
расходиться с живой => риск ложного пропуска modify.
Решение: pre-check вообще не запрашивает схему.
- modifierDesiredEqualsLive(ctx, uid, desired): сравнение по live-кодам
(live[lower(code)]), единственный источник — state.params.
- Значения: похожи на JSON ({/[) — смысловое сравнение; иначе скалярное с
нормализацией (null/"" -> ""; true/false без учёта регистра) — закрывает и
регистр bool.
- Fail-safe сохранён: пусто/нет кода/ошибка live => modify выполняется.
- Удалены: fetchOperationSchemaByID, modifierValuesEqual, lookupLiveParam больше
не участвует в pre-check (остаётся для досылки).
- Тесты переписаны (RawValuesEqual_Scalars/JSON, DesiredEqualsLive: 4 кейса).
Документация: TOOLS/ARCHITECTURE.md -> новый раздел «Modifier Idempotency»
(5 правил контракта); HISTORY — журнал раунда 5.
Проверено: go build ./... OK; go test ./internal/... -short PASS.
This commit is contained in:
@@ -106,4 +106,39 @@ Required с пустым `data_type` (все получают `""`; угадыв
|
||||
|
||||
**Осталось:** замер владельцем (`terraform plan` ×2 на `DEV_STAND/FullPipe`); B8 (`modifiers.yaml`).
|
||||
|
||||
---
|
||||
|
||||
## Раунд 5 — код-ревью (Opus) и его фиксы
|
||||
|
||||
Промпт: `NOTES/20_prompts/prompt_for_opus_code_review_round5.md`. Opus нашёл 6 пунктов; критичные — 2.
|
||||
|
||||
| # | Находка | Решение |
|
||||
|---|---|---|
|
||||
| 1 | **Регрессия B6:** pre-check стоял ПОСЛЕ `POST /instanceOperations` → при совпадении оставался «черновик» операции (pending). | Исправлено: pre-check до создания операции. |
|
||||
| 2 | Partial state: `resp.State.Set(&data)` мог записать unknown/computed → «invalid new value … unknown». | Исправлено: пишется только `id` (`SetAttribute`). |
|
||||
| 3 | 401 ретраится, но токен между попытками не обновляется. | Принято как есть; пояснено в комментарии `http.go`. |
|
||||
| 4 | `ImportState` vs `Read` — конфликта нет. | ок. |
|
||||
| 5 | `modifierDesiredEqualsCurrent` — мёртвый код (только тесты). | Удалено; тесты переведены на живые функции. |
|
||||
| 6 | Сравнение по `default`-схеме рискует ложным пропуском (ревизия п.1). | Исправлено: pre-check **без схемы** — только live-коды. |
|
||||
|
||||
**Итоговый контракт pre-check (см. `TOOLS/ARCHITECTURE.md` → «Modifier Idempotency»):**
|
||||
1. pre-check выполняется **до** `POST /instanceOperations`;
|
||||
2. единственный источник — live (`state.params`), схема операции не запрашивается
|
||||
(`default/{opId}` может расходиться с живой; живая доступна только после создания);
|
||||
3. поиск значения — по самому коду (`live[lower(code)]`);
|
||||
4. fail-safe: пусто/нет кода/ошибка live → modify выполняется;
|
||||
5. сравнение: похоже на JSON — смысловое (порядок ключей не значим), иначе — скалярное
|
||||
с нормализацией (`null`/`""` → `""`; `true`/`false` без учёта регистра).
|
||||
|
||||
Файлы: `core/modifier_compare.go` (новые `modifierRawValuesEqual`, `looksLikeJSON`,
|
||||
`normalizeRawScalar`, `modifierDesiredEqualsLive` без схемы), `core/operation_run_bycode.go`,
|
||||
`core/operation_cfs.go` (удалена `fetchOperationSchemaByID`), `core/client_test.go`,
|
||||
`core/modifier_compare_test.go`, `TOOLS/ARCHITECTURE.md`.
|
||||
|
||||
Тесты: `TestRunInstanceOperationByIdempotent_SkipsWithoutCreatingOperation` (POST операции не
|
||||
вызывается при совпадении), `TestModifierRawValuesEqual_Scalars/JSON`,
|
||||
`TestModifierDesiredEqualsLive` (совпало / отличается / нет кода / live недоступен).
|
||||
Проверено: `go build ./...` OK, `go test ./internal/... -short` PASS.
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user