diff --git a/docs/superpowers/specs/2026-05-27-drawing-title-block-design.md b/docs/superpowers/specs/2026-05-27-drawing-title-block-design.md index ef10d9e..d8057b4 100644 --- a/docs/superpowers/specs/2026-05-27-drawing-title-block-design.md +++ b/docs/superpowers/specs/2026-05-27-drawing-title-block-design.md @@ -1,7 +1,18 @@ # Дизайн: основная надпись чертежа (drawing_fill_title_block) через API7 **Дата:** 2026-05-27 -**Статус:** реализовано и проверено (4 интеграционных + 7 unit, всего 188); ревью Codex спека учтено +**Статус:** реализовано и проверено (4 интеграционных + 7 unit, всего 188); ревью Codex спека и реализации учтено + +## Правки по ревью реализации Codex + +- **null-check ячейки** (#3): доступ к графе через `RequireStampCell(stamp, columnId)` — проверяет + `columnId > 0` и `Text[columnId] != null` с диагностикой; используется в записи и чтении (единое + место валидации, #6). +- **`columnId <= 0`** (#4): валидируется в `RequireStampCell` (защищает и `ReadStampCellAsync`). +- **Нетранзакционность** (#2/#5): записываем все графы, затем один `Update()`; при `Update()==FALSE` + откат старых значений не делаем — зафиксировано комментарием как осознанное поведение (откат + спекулятивен, сценарий невоспроизводим). +- **RCW** — осознанный долг (v2-2), точечный release не вводим. ## Находка спайка: `Str` ПЕРЕЗАПИСЫВАЕТ (Clear не нужен) diff --git a/src/Kompas.Mcp.Core/Drawings/DrawingService.cs b/src/Kompas.Mcp.Core/Drawings/DrawingService.cs index a0b3d9f..7a2d6b8 100644 --- a/src/Kompas.Mcp.Core/Drawings/DrawingService.cs +++ b/src/Kompas.Mcp.Core/Drawings/DrawingService.cs @@ -46,8 +46,11 @@ public sealed class DrawingService throw new ArgumentException("Укажите хотя бы одно поле основной надписи (designation/name/material)."); var stamp = RequireActiveStamp(); + // Нетранзакционно: все графы записываются, затем один Update() фиксирует. При Update()==FALSE + // (на практике не воспроизводится) откат старых значений не делаем — он спекулятивен (Str уже + // на in-memory объектах графы, повторный Update мог бы тоже упасть); сообщаем об ошибке. foreach (var (columnId, text) in fields) - stamp.Text[columnId].Str = text; // Str замещает содержимое (Clear не нужен) + RequireStampCell(stamp, columnId).Str = text; // Str замещает содержимое (Clear не нужен) if (!stamp.Update()) throw new InvalidOperationException("IStamp.Update вернул FALSE — основная надпись не обновлена."); return fields.Count; @@ -58,9 +61,18 @@ public sealed class DrawingService => _dispatcher.InvokeAsync(() => { var stamp = RequireActiveStamp(); - return stamp.Text[columnId]?.Str ?? string.Empty; + return RequireStampCell(stamp, columnId).Str ?? string.Empty; }, ct); + /// Получить графу основной надписи по номеру с диагностикой (номер > 0, ячейка != null). + private static IText RequireStampCell(IStamp stamp, int columnId) + { + if (columnId <= 0) + throw new ArgumentOutOfRangeException(nameof(columnId), "Номер графы основной надписи должен быть > 0."); + return stamp.Text[columnId] + ?? throw new InvalidOperationException($"Графа {columnId} основной надписи недоступна (Text[{columnId}] == null)."); + } + private IStamp RequireActiveStamp() { var doc2d = RequireActiveDrawing();