При переключении на ветку или коммит без plugin/skills junction'ы навыков
повисают битыми, и git checkout падает на середине: часть рабочего дерева
уже удалена, а HEAD остаётся на прежней ветке.
Remove-AgentSkillLink удаляет сам reparse point через Directory.Delete
(без -Recurse), поэтому в target не заходит и содержимое plugin/skills не
трогает. Junction опознаётся по атрибуту ReparsePoint, а не по резолву
target — иначе режим не работал бы ровно в том случае, ради которого сделан.
Настоящий каталог не удаляется, а возвращает Skipped.
Режимы скрипта разведены parameter set'ами: -Remove -Check вместе теперь
дают ошибку разбора параметров вместо тихого выигрыша одного из ключей.
Покрыто пятью кейсами в tools/tests/AgentAssets.Tests.ps1 (включая junction
с исчезнувшим источником); порядок действий и восстановление после падения
чекаута описаны в CLAUDE.md.
База docs/Kompas3D_SDK/ (2465 статей) переехала в приватный kompas-sdk-docs
и проиндексирована в RAG на CT 127; субагент kompas-sdk-research переведён
на MCP-инструменты сервера kompas-sdk.
Спек дополнен фактами прогона: 2465 документов / 13 755 точек, проверка
grep/search/фильтров, живой прогон CI с инкрементальной переиндексацией.
Отдельно зафиксирована находка — реранк падал 400 на длинных чанках
(слот 1024 токена против обрезки в 2000 символов); починено в rag-node.
Гейт «версия не должна уменьшаться» отвергал и равную версию безусловно, что
делало невозможным повторный прогон на том же теге (Task 10 Step 4 плана).
Теперь сравнение version+sourceSha читается из plugin/server.lock.json ветки
dist: равная версия проходит дальше только если sourceSha из dist совпадает с
текущим github.sha (ретрай уже опубликованного релиза — ниже по пайплайну он
и так идемпотентен: upsert релиза/ассета, повторная сверка sha256, force-push
dist тем же содержимым). Другой sourceSha при той же версии — попытка
подменить уже опубликованный релиз другим кодом — отвергается с явным
сообщением, какой коммит уже занимает эту версию. Меньшая версия отвергается
как и раньше.
ci.yml: сборка + unit-тесты на Linux-раннере (EnableWindowsTargeting), плюс
проверка согласованности plugin/.claude-plugin/plugin.json и
plugin/server.lock.json (версии совпадают; для опубликованного релиза —
sha256 непустой и 64 hex).
release.yml: на тег vX.Y.Z — публикация self-contained win-x64,
идемпотентный upsert релиза/ассета через Gitea API, сверка SHA256
опубликованного ассета перед обновлением, и force-push ветки dist поверх
коммита с тегом (server.lock.json + plugin.json). Отличия от черновика в
плане: устойчивый разбор "версия не должна уменьшаться" (не полагается на
особенность jq на пустом stdin), явные HTTP-коды вместо curl -f для
различения "релиза нет" от прочих ошибок API, persist-credentials: false
на checkout, чтобы не конфликтовать с собственным токеном на пуше.
Не прогонялось на раннере — раннер ещё не готов (отдельная работа).
server.lock.json вида "{}" (валидный JSON, ноль ключей) — реальный вход
(placeholder до заполнения релизным пайплайном), но под
Set-StrictMode -Version Latest член-перечисление .PSObject.Properties.Name
бросает PropertyNotFoundException, когда коллекция .Properties пуста, — Read-
KompasMcpLock падала сырым .NET-трейсом вместо дружественного сообщения.
Тесты этого не ловили: любой объект хотя бы с одним свойством (даже без
version) уже не задевает пустую коллекцию.
Фикс: PSObject.Properties['имя'] (индексатор) вместо .Properties.Name
-contains — он возвращает $null и не бросает независимо от того, пуста
коллекция или нет. Применено ко всем трём проверкам (version/url/sha256).
Отдельно исключён случай $lock -eq $null (JSON "null" и пустой файл
разбираются в $null, а $null.PSObject тоже бросает под StrictMode).
Добавлены тесты: лок "{}", лок "null", лок "" (пустая JSON-строка вместо
объекта) — все дают сообщение с именем файла, а не .NET-трейс. Известные
пробелы покрытия (IOException-гонка вокруг Directory.Move,
AbandonedMutexException) зафиксированы комментарием в тестовом файле — не
автотестируются осознанно (нужен настоящий межпроцессный тайминг).
- Test-Path -PathType Leaf: путь-каталог в KOMPAS_MCP_EXE больше не проходит
проверку молча — раньше падение случалось на голом & $exe сырым
CommandNotFoundException в обход диагностической обёртки.
- запуск сервера (& $exe @args) обёрнут в try/catch: сбой самого запуска
(битый бинарь, недостающая зависимость, отказ в доступе) теперь тоже даёт
чистое сообщение 'kompas-mcp launcher: ...' и код 1, а не сырой трейс;
ненулевой код возврата уже запущенного сервера по-прежнему пробрасывается как есть.
- тесты: три новых сценария (каталог вместо файла, ноль байт в stdout на
пути отказа, повреждённый exe) + первый тест переписан на побайтовую
проверку stdout через Start-Process без фильтрации пустых строк.
Три предметных замечания ревью качества:
- Resolve-KompasMcpExecutable: WaitOne() перенесён в try/finally, отдельно
ловится AbandonedMutexException (владелец умер посреди установки — владение
переходит к нам, это не ошибка); ReleaseMutex вызывается только если mutex
реально захвачен, иначе он бросает SynchronizationLockException и маскирует
первопричину. Добавлен -Downloader passthrough для тестируемости.
- Read-KompasMcpLock: разбор JSON обёрнут в try/catch с сообщением, называющим
файл; наличие полей version/url/sha256 проверяется через
PSObject.Properties.Name (обращение к отсутствующему свойству под
Set-StrictMode -Version Latest бросает PropertyNotFoundException); sha256
валидируется на 64 hex-символа.
- Добавлены тесты на конкурентную логику: Resolve-KompasMcpExecutable
(повторный вызов не скачивает), гонка "сосед выиграл" (целевой каталог уже
содержит годную/негодную установку) в Install-KompasMcpServer, счастливый
путь и все новые ветки валидации в Read-KompasMcpLock.
Мелочи: & $Downloader ... | Out-Null (внедряемый загрузчик не должен писать
в success stream); уточнён комментарий Test-KompasMcpInstall — оно намеренно
трактует любую ошибку чтения маркера (не только битый JSON, но и блокировку
антивирусом) как "установка негодная".
AbandonedMutexException-ветка вручную воспроизведена и проверена: процесс-
владелец mutex убит, пока другой процесс уже блокирован в WaitOne() —
тот получает AbandonedMutexException и корректно продолжает работу.
MD-база docs/Kompas3D_SDK/ (2465 статей, 10.4 МБ) переехала в приватный
kompas-sdk-docs и проиндексирована в RAG на CT 127. Доступ — MCP-сервер
kompas-sdk (порт 8092); субагент kompas-sdk-research переведён с Grep/Read
на его инструменты и теперь ищет по базе, которой в репозитории нет.
Порядок поиска в инструкции агента: grep_knowledge для точных имён,
search_knowledge с фильтрами type/tags для задачных вопросов,
get_chunk_context вместо get_document (геометрия.md весит 261 КБ).
Обновление при новой версии КОМПАС — update_sdk_docs.ps1 в репозитории
доков: zip справки → генератор → diff → тег → push → переиндексация
через Gitea Actions. Спек и фактические результаты —
docs/superpowers/specs/2026-07-31-sdk-docs-rag-migration-design.md.
Прежний комментарий утверждал, что Global\ требует отдельной привилегии —
ревью опровергло это эмпирически (мьютекс создался без ошибок под тем же
непривилегированным пользователем). Настоящая причина: плагин ставится на
чужие незнакомые машины, где политика безопасности может отличаться, и отказ
Global\ был бы фатальным для запуска сервера, тогда как остаточный риск
сессионного mutex (лишняя повторная загрузка при параллельных сессиях)
ограничен и не портит установку.
- AgentAssets.psm1 и sync-agent-assets.ps1 пересохранены в UTF-8 с BOM:
powershell.exe читает файлы без BOM в системной ANSI-кодировке и ломает
парсинг на кириллических строковых литералах (pwsh 7 работал за счёт
автодетекта и маскировал баг).
- Copy-Item в ветке -AllowCopy получил -ErrorAction Stop: без него ошибка
копирования писалась как non-terminating и функция возвращала 'Copied'
при частично скопированном каталоге, если вызывающий не выставил
$ErrorActionPreference='Stop' сам.
- Test-AgentSkillLink сравнивает пути через
[string]::Equals(...,OrdinalIgnoreCase) явно, а не через регистронезависимый -eq.
- Добавлены тесты: отсутствующий SourcePath, перелинковка на другой
источник (старый target остаётся нетронутым) и AllowCopy-регрессия
(мок Copy-Item с [CmdletBinding()], чтобы корректно эмулировать
ErrorAction реального cmdlet).
Инкремент 9. 83 инструмента, 331 тест. + заметка о ревью реализации в спеке
(Codex + pi/glm-5.1 + pi/kimi-k2.6).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- проверка возврата 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>
Инкремент 8. 82 инструмента, 308 тестов. + заметка о ревью реализации в спеке
(Codex + pi/glm-5.1 + pi/kimi-k2.6; K1 снят спайком — КОМПАС сам выводит ориентацию user).
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>
Дизайн и план реализации нового навыка kompas-fdm-design — методика
проектирования деталей под FDM-печать (правила DFM + лёгкий гео-аудит),
отдельный от kompas-3d, без слайсера, экспорт через export_step.
Выверено консультациями pi (glm-5.1, kimi-k2.6) и 6 ревью (spec x3, план x3).
Сам навык (SKILL.md + references/) по плану — РЕАЛИЗАЦИЯ ПРЕДСТОИТ.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>