Протокол второго прогона (48 вызовов, фаска по всему контуру надписи одной операцией):
feature_delete эскиза уносит и смещённую плоскость, созданную ради него, — её id наружу
не выдавался, и в дереве оставалась «Смещенная плоскость:N», которую нечем убрать.
list_edges отдаёт разбивку по типам и готовую строку индексов; при >60 рёбрах печатает
только их. Раньше субагент звал инструмент шесть раз, перебирая type, чтобы убедиться,
что не пропущен тип кривой.
Навык: исправлено МОЁ ЖЕ утверждение из прошлой итерации — карман subtract опасен для
глифов (высота 3 мм), а не для каймы (2 мм, в слой кармана не попадает); запрет на пакетную
обработку рёбер текста сужен до скруглений верхних рёбер — фаска по 59 рёбрам нижнего
контура строится с первой попытки; добавлены формула кегля через одну пробу и кайма
для прямоугольной плашки вторым примитивом.
По протоколу регресс-прогона (46 вызовов против 90 в прошлом):
feature_delete — ksDocument3D.DeleteObject + RebuildDocument, зависимые уходят каскадом.
Неудачная проба больше не требует пересобирать документ (у субагента таких кругов было три).
align/vAlign у примитива text: сервер строит, замеряет и сдвигает надпись сам. Ловушка,
стоившая двух заходов: ksMoveObj и ksDeleteObj по ссылке от ksConvertTextToCurve возвращают
успех и не делают ничего — двигать надо по ссылке ТЕКСТА, а габарит после сдвига считать
арифметикой (по текстовой ссылке рамка идёт по ячейке). Поймано тестом, который сверяет
заявленный габарит с выдавленной геометрией.
list_edges: отбор по типу и координатному окну + концы рёбер в списке. Так адресуется
нижний контур под фаску, который иначе не выбрать: индексов не отфильтровать, точек не угадать.
Навык: wf меряет пропорцию крупно (1.0→1.25 = +29 %), но квантован ступенями — точную ширину
набирают height; прирост каймы указан НА СТОРОНУ и зависит от начертания, а не от шрифта;
добавлено предупреждение, что primitive(subtract) режет наехавшие буквы.
ksConvertTextToCurve ВОЗВРАЩАЕТ ссылку на кривые, и только по ней ksGetObjGabaritRect
даёт габарит букв: по текстовой ссылке это ячейка строки (Arial h=10 «HH» — 23.75 против
19.97 у глифов, второе совпадает с выдавленным телом). Агент вписывает надпись в поле
по габариту, поэтому в ответе теперь оба числа.
OutputPath.Prepare создаёт каталог назначения и делает путь абсолютным (document_save,
export_step, снимок): КОМПАС в несуществующую папку молча не пишет, а диагностика при
этом винила занятое имя файла — чинили не ту причину. Причины разведены.
Имя (name) прокидывается до создания объекта: API5 — ksEntity.name перед Create(),
API7-пути (примитив, отверстие, булева, рез, перенос грани) — IModelObject.Name перед
Update(). Читается назад через ksFeature.name, который только на чтение. Без имени
дерево состоит из «Эскиз:1» и «Элемент выдавливания:3».
fillet_edge/chamfer_edge принимают список точек: на теле с сотнями рёбер list_edges
неподъёмен, а выбор по точке брал одно ребро — четыре угла давали четыре операции.
Ненайденные рёбра перечисляются ВСЕ сразу, модель при этом не мутируется.
set_part_info задаёт наименование и обозначение детали: без него в дереве
стоит безликая «Деталь», и она же уходит в штамп чертежа и в спецификацию.
describe_model показывает наименование в шапке, а безымянную деталь помечает
явно. Имя файла этого не заменяет — это разные свойства.
document_save рапортовал «Сохранено», ничего не записав: SaveAs возвращает
void и молча отказывает, если файл с этим именем уже открыт в КОМПАС другим
документом. Теперь путь и наличие файла проверяются после записи, а Save
сверяет флаг Changed. Освободить занятое имя нечем не было — document_close
получил режим all.
Текст исключения доходил до клиента только у McpException, всё остальное
подменялось на «An error occurred invoking». То есть подсказки в наших
сообщениях агент не видел вовсе. Фильтр CallTool в Program.cs возвращает
IsError с реальной причиной (ToolErrorText разворачивает AggregateException
и склеивает вложенные причины). Там же — регистрация PrimitiveService.
Три способа получить форму там, где раньше был только замкнутый контур
из отрезков и дуг.
1. Примитив эскиза type=text: ksTextEx + ksConvertTextToCurve. Пока текст
остаётся текстом, это оформление, и операция его не видит; после
конвертации глифы становятся обычным сечением. Ответ возвращает
фактическую длину строки — иначе ширину шрифта до выдавливания не узнать.
2. extrude(thinThickness, thinSide): контур трактуется как стенка заданной
толщины. outward по замкнутому контуру даёт кайму-эквидистанту вокруг
него — так надпись получает подложку без 2D-эквидистанты.
Толщину при dtReverse КОМПАС читает из reverseThickness: положить её в
normalThickness значит молча получить СПЛОШНОЕ сечение вместо рамки.
3. Инструмент primitive: элементарные тела API7 (block, cylinder, sphere,
cone) с result=new|union|subtract|intersect. Карман и паз строятся
вычитанием тела, без эскиза и выреза. Update() возвращает TRUE даже
когда вычитание прошло мимо тела, поэтому сервис сверяет объём до и
после и откатывает операцию, если ничего не изменилось.
Инструменты были нарезаны по способу вызова, а не по смыслу: четыре отверстия,
двойники *_index, одиннадцать sketch_add_*. Агент платил за это дважды — 40 КБ
описаний в каждой сессии и лишние round-trip'ы, а каждый вызов это ещё и шанс
сбиться. Теперь инструмент называет ОПЕРАЦИЮ, вариант задаётся параметром,
объект выбирается индексом или точкой одним и тем же инструментом.
Проверка построения приходит сама. Каждая мутирующая операция дописывает к ответу
итог validate_part (AutoValidation): Create()/Update()==true не значит успех, а
правило навыка «проверяй после каждого шага» удваивало число вызовов. Выключается
через set_auto_validate или KOMPAS_MCP_AUTOVALIDATE=0 — сбой самой проверки уходит
в примечание и никогда не превращает удачную операцию в ошибку.
Эскиз: 16 инструментов → 3. sketch_create(plane|faceIndex|x,y,z, entities[],
autoClose) строит контур целиком; пакет выполняется за ОДИН заход на STA-поток
(PartModeler.AddEntitiesAsync), ошибка называет позицию примитива в списке.
Слияния: hole(type=simple|counterbore|countersink|conic), extrude/revolve(mode),
pattern(kind), mirror (без featureIds — всё тело), document_save(path?),
set_variable как upsert (разведочный вызов «есть ли такая» больше не нужен),
list_faces/list_edges(index?) вместо отдельных describe_*, get_part_info и
get_bounding_box — в describe_model(sections), где незапрошенные разделы вообще
не читаются из модели.
Селектор index|point: fillet_edge/chamfer_edge принимают edgeIndices списком —
одна операция дерева на все рёбра; для операций API7, умеющих только точку,
индекс переводится в точку через ModelInspectionService.FaceCenterPointAsync
(середина параметрической области грани).
Схема слитого инструмента не запрещает неверную комбинацию полей — это делает
валидация, и её сообщение называет type и недостающий параметр.
Тесты: 291 unit (+41) и 136 integration (+5), интеграционные — на живом КОМПАС.
Новые проверяют ровно рискованные места: точка-из-индекса лежит на грани и по ней
создаётся эскиз, мульти-ребёрное скругление даёт один узел дерева и убыль объёма,
upsert создаёт и затем меняет переменную с формулой, пакет сообщает позицию сбоя.
PluginSkillsTests теперь падает, если в публикуемом навыке всплывёт слитое имя.
Продолжение 4d4e114 (сборки КОМПАС убраны из поставки).
- kompas_status проверяет интероп до обращения к сессии и отдаёт причину текстом:
иначе MCP SDK показывает агенту только «An error occurred invoking …»
- провайдер сносит негодный остаток кеша перед Move: повтор после оборванной
распаковки падал IOException вместо внятного сообщения
- README/plugin/README/doctor/CLAUDE.md: требование компонента SDK,
KOMPAS_INTEROP_DIR, кеш в %LOCALAPPDATA%, счётчики тестов 381 (250 unit)
- проверка возврата AddBranchByPoint/SetBranchTextPosition (bool) — ранняя диагностика
вместо позднего RPC_E_SERVERFAULT (Codex, glm)
- RU-алиасы в сообщении ShelfDirections.Parse и в описании инструмента (glm, kimi)
- интеграционный тест направления полки параметризован Right/Left/Up/Down — покрывает
всю поверхность ToKompas на реальном COM (glm, kimi)
- отклонено (обоснование): unit ToKompas (граница проекта), e2e tool-обёртки (тонкая),
angleDeg (нет Angle у ILeader), default text у AddText (вне объёма)
331 тест зелёный, сборка Release чистая.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- K1 (kimi, Major): спайк подтвердил — для User КОМПАС САМ выводит VerticalOrientation
из W/H (игнорирует флаг, W/H не свопает); read-back корректен, правка не нужна —
задокументировано комментарием + тесты на Landscape (500×300→альбомная, 300×500→книжная)
- K2: ValidateFormatDimensions называет точный нарушивший параметр (width/height)
- тесты: A1 в Parse (C3), A4 landscape со свопом 297×210 (G2), sheetNumber=0 (C2/G3)
- отклонено (нит): алиасы в описании (G4), сообщение NaN (G5), тип исключения standard (K5)
308 тестов зелёных, сборка Release чистая.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- описание (x3,y3) и сообщение об ошибке: угол выбирает angleType, не точка дуги
- тест angleType=Max (лучи 0°/45° → 135° тупой супплемент): min=45/max=135/more=315
- спек дополнен находкой о трёх типах угла
241 тест зелёный, сборка Release чистая.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#1: непустота проверяется у ВНОВЬ созданных видов (разница IView.Number), не по
всей коллекции — прежняя геометрия чертежа не маскирует пустые новые виды.
#3: откат созданных видов (IView.Delete) при created!=3 или пустых видах.
#7: unit-тест нормализации кириллического пути. #4: повторный вызов (дубликаты)
отражён в описании инструмента. #5/#6/#2 — по обоснованию (release RCW отклонён v2-2).
Тесты: 116 unit + 61 integration = 177 зелёных.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Базовый класс IntegrationTestBase (IAsyncLifetime) закрывает все открытые
документы КОМПАС после КАЖДОГО теста (DocumentService.CloseAllAsync) — чтобы не
копились вкладки-пустышки и упавший Assert не оставлял открытый документ. Все
интеграционные тест-классы переведены на него (: base(fx)); индивидуальные
finally CloseAsync сохранены как первичная очистка.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- проверка возврата ksPart.RebuildModel() во всех операциях (не рапортовать успех при неудаче) — P2
- сообщение об удалении: намёк на зависимые переменные — P3
- дисклеймер про геометрию в описании set_variable + десятичный разделитель — P3
- тест дополнен: дубликат имени, удаление с зависимыми, десятичное выражение (3.5), пересчёт зависимой
- 116 тестов зелёных
- VariableService: CreateVariableAsync/SetVariableAsync/DeleteVariableAsync/GetVariableValueAsync
- создание ТОЛЬКО на ksFeature.VariableCollection (ksPart — только внешние); RebuildModel + re-fetch
- Expression — ведущее поле (константа/формула со ссылками); value пересчитывается
- list_variables (ModelInspectionService) исправлен: читает feature-коллекцию (все переменные)
- инструменты create_variable/set_variable/delete_variable (VariableTools) + DI
- интеграционный тест CRUD round-trip (создание, формула width*2, удаление по зависимостям)
- ОГРАНИЧЕНИЕ: переменные драйвят геометрию лишь в параметрической модели (эскизы на литералах — нет)
- 116 тестов; спек docs/superpowers/specs/2026-05-27-parametrics-design.md
- HoleService (Core/Modeling): простое цилиндрическое отверстие (ksHTBase), сквозное/глухое
- размещение: BaseSurface=грань (FindObjectsByPoint) + AssociationVertex=Points3D.Add()
(точка по мировым координатам ksPParamCoord+X/Y/Z); эскиз размещения не нужен
- в API5 определения отверстия НЕТ (как draft) — реализовано на API7 (прецедент: move_face)
- авто-направление в тело с повтором; DI-регистрация; инструмент hole в FeatureTools
- интеграционные тесты: сквозное (Ø10 удаляет π·25·20) + глухое (Ø8×10); spike зелёный с первого раза
- 103 теста зелёных; спек docs/superpowers/specs/2026-05-27-hole-design.md
- mirror_body: ChooseBodies() в interop не приводится к ksChooseBodies (null) и не нужен —
o3d_mirrorAllOperation по умолчанию отражает все тела, сохраняя оригинал (проверено тестом);
убран нерабочий вызов (P2 fail-fast вскрыл, что приведение всегда падает)
- добавлен тест CircularPattern_reverse_flips_rotation_direction (проводка inverce) — P2
- комментарий класса FeatureOpsTests: пакеты B и C — P3
- 101 тест (60 unit + 41 integration)
- CreateSketchOn проверяет entity.Create() (улучшает все пути создания эскиза)
- loft-сообщение считает сечения после дедупликации
- тест loft: проверка габарита 20x20x40
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>