Releases: Desko77/ai-edt
Release list
AI-EDT v0.2.38
- Квалификатор входит в запрос
- Аргумент, который операция не читает
- Значение, равное умолчанию модели
- Отчет платформы и отказ импорта
- Ход долгого прогона
Квалификатор входит в запрос
Названный квалификатор входит в запрос на применение типа: при precision, fractionDigits, length, allowedLength, dateFractions или nonNegative элементы типа пересобираются вместе с квалификатором. Совпадение имен типов приводит к пропуску только тогда, когда ни один квалификатор не назван.
fractionDigits=0 и nonNegative=false считаются заданными значениями.
Ответ различает applied и idempotentSkip.
Затрагивает set_object_type, типы полей external_data_source_workshop и типы реквизитов формы.
Аргумент, который операция не читает
edit_metadata отклоняет аргумент, которого операция не читает. Отказ называет отклоненный аргумент и перечисляет те, которые операция читает.
Служебные ключи вызова не проверяются: operation, dryRun, batch, operations, stopOnError, runKey, timeoutSeconds, topic, confirm, projectName.
Проверка применяется к операциям с известным набором параметров.
Значение, равное умолчанию модели
Ответ на запись свойства несет serializedAsDefault, когда записанное значение равно умолчанию модели: свойство задано, тега в файле не будет. Признак читается через EObject.eIsSet после записи.
Затрагивает set_object_property и set_form_item_property.
Отчет платформы и отказ импорта
Отказ сборки внешнего объекта несет отчет платформы целиком, а не его первую строку. Строки соединяются через |, одинаковые не повторяются, длина ограничена 700 знаками.
import_external_object при отсутствии результата сообщает установленное: вызов вернулся без ошибки, объект в проекте не появился. Отказ указывает журнал рабочей области .metadata/.log, где идет запись преобразования бинарника в XML.
Ход долгого прогона
Ответ status=Pending у edit_metadata batch несет progress: сколько операций выполнено, сколько применено, сколько отклонено и какая идет сейчас. Тот же текст приходит в statusMessage задания при опросе через tasks/get.
Одиночная операция edit_metadata, вышедшая за ожидание, отвечает status=Pending с runKey. Результат забирается повторным вызовом с этим ключом, работа при этом не перезапускается.
runKey и timeoutSeconds объявлены в схеме edit_metadata. Ожидание - от 5 до 120 секунд, по умолчанию 25.
AI-EDT v0.2.37
- Отказ приходит текстом
- Константа перечисления по обоим написаниям
- Именованные области макета
- Формы
- Общий модуль и булевы свойства
- Batch
Отказ приходит текстом
Ответ инструмента с типом ответа MARKDOWN при отказе содержит текстовый блок. Успешный ответ по-прежнему содержит вложенный ресурс, режим plain-text не изменён.
Затрагивает write_module_source во всех режимах, read_method_source и остальные инструменты, отвечающие markdown.
Константа перечисления по обоим написаниям
BmFormHelper ищет константу перечисления EDT по литералу и по имени Java-константы, без учёта регистра. Перечисление, сгенерированное EMF, возвращает из toString() литерал (Label, Picture, Left), а имя константы - LABEL, PICTURE, LEFT.
add_decoration elementType=Label записывает <type>Label</type> рядом с LabelDecorationExtInfo. Тем же исправлены ветка Picture и выравнивание надписи влево.
Именованные области макета
mxl_workshop принимает add_named_area, list_named_areas и remove_named_area. Параметры: areaName, areaKind (columns, rows, rect) и границы fromRow / fromCol / toRow / toCol, 1-базные.
read_template возвращает namedAreas.
Имя, названное дважды, перемещает область: SpreadsheetDocument.getNamedItems() - это EMap, и запись по существующему ключу заменяет значение. Удаление области, которой нет, возвращает отказ.
Формы
add_group elementType=Pages создаёт PagesGroupExtInfo. add_group elementType=Page заполняет PageGroupExtInfo значениями group=Vertical и showTitle=true.
add_table autoGenerateColumns=true берёт заголовок колонки из getTitle, затем из getSynonym реквизита; при их отсутствии <title> не записывается.
add_form_event_handler знает события Clearing, TextEditEnd и Opening - 24 события формы. Каждое принимает Элемент первым аргументом обработчика.
Справка edit_form по addButton называет имя создаваемой команды: оно совпадает с именем кнопки.
Общий модуль и булевы свойства
set_object_property и set_form_item_property отклоняют вызов, если значение не передано, а сеттер принимает примитив; отказ называет свойство, требуемый тип и параметр propertyValue.
create_object objectType=CommonModule принимает externalConnection, clientOrdinaryApplication и serverCall. Все шесть флагов контекста объявлены в схеме edit_metadata. server включается, когда ни один другой контекст не включён.
Batch
edit_metadata batch=true наследует formFqn наравне с projectName, ownerFqn и dryRun.
Отказ батча перечисляет упавшие операции с причиной каждой - до пяти, дальше счёт остальных.
AI-EDT v0.2.36
- Реестр поддержки поставщика
- Трёхстороннее сравнение и объединение
- Диагностика расширения перед обновлением
- Отказ валидатора на непостроенной модели
- Поле isError в ответе
- Схемы СКД
Реестр поддержки поставщика
Инструмент support_registry. Операции: status, list_objects, object_mode, snapshot_modes, restore_modes, help.
Возвращает список конфигураций-поставщиков и их релизы, распределение объектов по UserSupportMode (ChangesNotAllowed, ChangesAllowed, Cancelled), правила следующего обновления, режим отдельного объекта и объекты, изменяемые вместе с ним. Учитываются подчинённые сущности: реквизиты, табличные части, формы, макеты.
list_objects принимает оба написания режима, ChangesAllowed и CHANGES_ALLOWED. Значение, не соответствующее ни одному режиму, даёт отказ.
snapshot_modes записывает соответствие «объект - режим» в файл в .settings. restore_modes возвращает записанные режимы; без apply=true не изменяет ничего. Объект, отсутствующий в новой поставке, попадает в список отказов.
Трёхстороннее сравнение и объединение
compare_three_way сравнивает открытый проект, новую поставку и общего предка.
intent: REPORT - чтение, MERGE - применение решений, UPDATE_UNCHANGED - взятие поставки целиком, UPDATE_KEEPING_OURS - обновление на поддержке, при котором узлы с атрибуцией VENDOR берутся по recommendedRule, а узлы с атрибуцией OURS и BOTH получают MergeRule.DO_NOT_MERGE.
methodLevel=true даёт узлы уровня метода: изменение приходит с полным именем вида CommonModule.X.Module.ИмяМетода и собственной атрибуцией. Решение о классе объектов задаётся записью {"select":"matching","rule":"..."}; правило каждого узла перечитывается после установки.
Узел с атрибуцией BOTH при сравнении без methodLevel получает MergeRule.DO_NOT_MERGE. Содержимое, заменённое объединением, перечисляется в ourContentLost и считается по каталогу объекта целиком.
Объединение не запускается при незавершённом построении модели и отказывает, если снимок режимов поддержки не записан. Смена версии платформы возвращается в ответе. Предок сверяется на происхождение от этого проекта.
Диагностика расширения перед обновлением
Возвращается список объектов расширения, которые после обновления перестанут применяться, с причиной по каждому.
Проверяется наличие цели перехвата и соответствие её сигнатуры. Заимствованный объект сверяется с поставкой пообъектно. Тип заимствованного реквизита читается из .mdo расширения.
Применимость расширения возвращает checkConfigurationExtensionsApplicability. borrow_child заимствует указанный дочерний объект. Перемещение объекта возвращает список перемещаемого вместе с ним и отдельно - виды ссылок, которые обход не покрывает.
Отказ валидатора на непостроенной модели
validate_query возвращает отказ с указанием причины, пока ProjectStateGuard.checkReadyOrError сообщает состояние, отличное от READY. Состояния: READY, BUILDING, NOT_AVAILABLE, UNKNOWN.
Проверка выполняется под IDerivedDataManager.addStatusListener и повторяется один раз при срабатывании слушателя.
Запрос в расширении проверяется моделью расширения и моделью родительской конфигурации. При недоступной модели родителя вызов возвращает отказ.
Поле isError в ответе
ToolCallResult возвращает поле isError при отказе инструмента. Поле сериализуется только при значении true.
Текст инструментов не изменён, YAML-заголовок успешного ответа сохранён, проверки на ведущее Error: работают.
Схемы СКД
dcs_workshop проверяет запрос или выражение перед записью на всех путях записи: set_dataset_property с property=query, set_calculated_field, set_total_field, add_user_field, set_user_field, обе стороны связи наборов данных. Свойство связи распознаётся по окончанию имени на Expression.
add_total проверяет aggregateFunction(expression) - текст, который записывается.
Диагностика с кодом Field not found в выражении понижается до WARNING. XtextSyntaxDiagnostic и org.eclipse.xtext.diagnostics.Diagnostic.Linking остаются ошибками. QlIssue возвращает код рядом с сообщением.
AI-EDT v0.2.35
Исправление, которое нельзя выполнить, теперь так и говорит; справка отвечает по одной операции; найден класс параметров, недостижимых через фасад.
- Исправление против действия интерфейса
- Справка по одной операции
- Параметры, которых клиент не мог обнаружить
- Чего в выпуске нет
- Проверено на стенде
Исправление против действия интерфейса
EDT предлагает на маркере не только правки. Часть вариантов - действия IDE: «Открыть панель структуры документирующего комментария» ничего в исходнике не меняет, а открывает панель. Выполнение такого варианта вне потока отрисовки бросает SWTException: Invalid thread access, и marker_corrections operation=apply отдавал это как The correction failed: Invalid thread access.
Теперь ответ называет, что произошло: вариант является действием интерфейса, выполнить его может только человек в редакторе, ничего не изменено, находка стоит; поле refusedBy несёт тип и сообщение отказавшего исключения. Выполнение в потоке отрисовки решением не является - оно открыло бы панель в редакторе пользователя и не изменило бы файл.
Распознавание идёт по ТИПУ исключения (org.eclipse.swt.*) и только затем по подстроке сообщения: сообщения платформы локализованы.
operation=list больше не утверждает «N findings can be corrected» - формулировка обещала то, чего apply для такого варианта выполнить не может.
Справка по одной операции
edit_metadata operation=help topic=<имя операции> отвечает параметрами этой операции:
| Операция | Параметры |
|---|---|
create_object |
dryRun, name, objectType, projectName, synonym |
set_role_right |
cascadeDependencies, dryRun, ownerFqn, projectName, rightName, targetFqn, value |
Общая схема фасада перечисляет 113 параметров на каждый запрос.
Карта «операция -> параметры» выведена из исходников для всех 250 операций скриптом scripts/check-operation-params.py и упакована в плагин как ресурс. Способы вывода: делегирование берёт схему делегата плюс чтения переписывателя параметров; ветка, работающая на месте, - свои extract*Argument; ветка, получающая локальные переменные, разрешает их назад через два шага присваивания; крупнейший фасад маршрутизирует реестром обработчиков и разбирается отдельно, включая обработчики-пересыльщики.
Затвор в CI падает на операции, чьи параметры установить не удалось. Три операции с законным нулём перечислены с причиной (обработчик отказывает до чтения - MXL cell API отсутствует), пять инструментов вне охвата названы в отчёте docs/tools/operation-parameters.md.
Параметры, которых клиент не мог обнаружить
Схема - единственное место, откуда клиент узнаёт, что параметр существует. Карта показала 47 параметров, которые обработчики читают, а схемы фасадов не объявляют.
Пять из них - ancestorPath, decisions, decisionsPath, decisionsFrom, intent у insights: compare_three_way не мог объединять через фасад, то есть через ту поверхность, которую показывает пресет по умолчанию. Они объявлены.
Остальные 42 записаны в scripts/unadvertised-parameters.txt как планка, которая может только уменьшаться: затвор падает на новом параметре, а не на унаследованных.
Чего в выпуске нет
Параметры из общей схемы не убраны. Наличие справки по операции этого не делает безопасным: клиент, который справку не спрашивает, составляет вызов только по схеме, и обмен контекста на вызовы, которые нельзя написать, - неверный обмен.
Проверено на стенде
EDT 2026.2.0+289.
| Проверка | Результат |
|---|---|
apply варианта-действия интерфейса |
отказ объясняет причину, refusedBy: SWTException: Invalid thread access |
list на проверках без исправлений |
«none has a correction ... and this is not a failure» |
help topic=create_object |
5 параметров |
help topic=no_such_thing_at_all |
отказ перечисляет темы и говорит про имена операций |
| Именованные темы справки | отвечают по-прежнему |
Сборка - 1645 тестов, семь затворов.
AI-EDT v0.2.34
Обновление на поддержке доведено до сценария, отвечают три базовых метода протокола, поиск больше не требует называть проект.
- Решения возвращаются от того, кто их принял
- Состояние проекта после объединения
- ping, resources/list, resources/read
- Поиск по всей рабочей области
- Проверено на стенде
Решения возвращаются от того, кто их принял
compare_three_way получил decisionsFrom - путь к файлу настроек, записанному раньше этим же инструментом или человеком в EDT. Восстанавливаются обе половины файла: правила объединения и соответствия объектов, установленные вручную между теми, что не совпадают ни по UUID, ни по имени.
Затвор объединения считал только решения, переданные в том же вызове, поэтому объединение по файлу отклонялось с decided=0, пока окружение уже держало правила. Теперь затвор видит оба источника, а отказ называет оба пути входа. Файл, который разобрался и не нёс решений, отличается от применённого полем decisionsRestored.
decisionsPath проверяется на расширение .zip до вызова: окружение требует его внутренним Assert.isTrue, и без проверки отказ приходил как неудачная запись файла со ссылкой на ограничение, о котором вызывающему не сообщали.
Состояние проекта после объединения
При intent=MERGE затронутые объекты перевалидируются, и ответ несёт errorsAfterMerge и revalidatedAfterMerge. Взятие чужой версии объекта штатно ломает то, что ссылалось на прежнюю, поэтому ответ, останавливающийся на merged, верен и бесполезен.
Когда решения пришли файлом и списка объектов в вызове нет, объекты берутся из самого сравнения - иначе без проверки оставались бы ровно те объединения, что подготовлены вручную. Счёт ошибок по объектам реализован в одном месте, ProjectProblemsReader.countErrorsOn, и отвечает -1, когда его не удалось взять: ноль выглядит как чистый проект.
ping, resources/list, resources/read
Три метода, прежде отвечавшие -32601.
ping входит в базовую спецификацию с первой ревизии - клиент, проверявший живость стандартным способом, получал отказ.
resources/list и resources/read отдают описания проверок EDT: 167 документов под схемой aiedt://checks/<id>, text/markdown. Схема своя, а не file:, потому что описание может лежать внутри jar-а плагина, где открывать клиенту нечего. Список строится тем же CheckDocReader, который их читает, поэтому перечислить нельзя то, чего затем не выдать.
Возможность resources объявлена и в рукопожатии, и в server/discover. Подписка и уведомления об изменении списка не объявлены: сервер их не шлёт.
Поиск по всей рабочей области
У search_in_code (code_search operation=text_search) projectName стал необязательным. Без него ищутся все открытые проекты с исходниками, и ответ называет их поимённо.
Все корни идут в один сборщик, поэтому предел результатов и бюджет времени значат одно и то же при любом числе проектов; повторный проход с гибкими пробелами идёт по тем же корням. Явно названный несуществующий проект остаётся ошибкой.
Проверено на стенде
EDT 2026.2.0+289, рабочая область с конфигурацией, её расширениями и вторым комплектом БСП - 11 проектов.
| Проверка | Результат |
|---|---|
| Решение на родителе | синоним реквизита табличной части тремя уровнями ниже принял сторону поставки |
Объединение по одному файлу, без аргумента decisions |
decisionsRestored=true, merged=true, проект принял другую сторону |
Файл отсутствует / расширение не .zip |
обе ветки отказа называют путь и причину |
| Состояние после объединения | errorsAfterMerge=0, revalidatedAfterMerge=true |
ping |
пустой результат, обе ревизии протокола |
resources/list / resources/read |
167 документов, markdown отдан |
| Поиск по одному проекту против всей области | тот же запрос: 0 совпадений против 209 в 32 модулях |
Сборка - 1640 тестов.
AI-EDT v0.2.33
Сравнение конфигураций тремя сторонами научилось применять принятые решения к проекту.
- Объединение по решениям
- Что считается признаком того, что объединение произошло
- Проверку выполняет окружение
- Проверено на стенде
Объединение по решениям
У compare_three_way появился параметр intent:
| Значение | Что делает |
|---|---|
REPORT |
по умолчанию, читает и ничего не меняет |
MERGE |
применяет решения из decisions к проекту |
MERGE_IGNORING_PROBLEMS |
идёт дальше проблемы, которую окружение назвало блокирующей |
Нераспознанное значение отклоняется с перечислением допустимых, а не читается как REPORT. Объединение без decisions отклоняется - применять нечего. MERGE_IGNORING_PROBLEMS - отдельное значение, а не флаг рядом с MERGE.
Под пресетом без права записи intent=MERGE отклоняется до того, как что-либо сравнивается: затвор проверяет способность write_module_source.
Что считается признаком того, что объединение произошло
IComparisonManager.startMerge возвращает Status.OK_STATUS в момент приёма задания: startMergeProcess -> startBatchMerge -> MergeProcessJob.schedule(). Из этого статуса не следует, что записано хоть что-нибудь.
ComparisonManager.performMerge ставит MERGE_PROCESS_FINISHED и следом вызывает discardSession, поэтому у завершённого объединения getStatus(handle) отдаёт null. Ожидание построено на этом: исчезновение сессии после принятого startMerge считается завершением, и поле merged берётся оттуда, а не из статуса планирования.
Терминальные состояния различаются: MERGE_PROCESS_DISCARDED и COMPARISON_MERGE_PROCESS_CANCELLED отдаются как отказ с пометкой, что не записано ничего. Объединение, пережившее отведённое время, не отменяется - ответ говорит, что работа продолжается и её исход отсюда не виден.
Проверку выполняет окружение
getMergeProblems(handle) законен только при MERGE_PROCESS_VALIDATION_FINISHED, MERGE_PROCESS_USING_EXTERNAL_TOOL_FINISHED и MERGE_PROCESS_USING_EXTERNAL_TOOL_CANCELED - до старта объединения он падает на Assert.isLegal. Собственная проверка блокирующих проблем перед стартом поэтому всегда видела ноль и всегда пропускала; она снята.
Гарантию даёт validateBatchMerge внутри самого задания: при непустом списке отказов главная фаза (performBatchMergeAsUserOperation) пропускается и запись не начинается. Проблемы читаются в единственный момент, когда доступны, и приходят вместе с отказом; поле problemsNote называет причину, если список прочитать не удалось - пустой список больше не может быть принят за «ничто не мешает».
Проверено на стенде
EDT 2026.2.0+289, проект-пробник, сравнение двух сторон.
| Проверка | Результат |
|---|---|
intent=MERGE_NOW |
отклонено с перечислением допустимых значений |
intent=MERGE без decisions |
merged=false, названа причина |
intent=MERGE, решение DO_NOT_MERGE |
merged=true, объект на месте |
intent=MERGE, решение GET_FROM_OTHER |
merged=true, .mdo проекта принял значение другой стороны |
Полное объединение заняло 2 секунды. Сборка - 1625 тестов.
AI-EDT v0.2.32
Сравнение конфигураций тремя сторонами, валидация запросов в расширениях и семь исправлений в том, как сервер сообщает о происходящем.
Что нового
Добавлено
compare_three_way, он жеinsights operation=compare_three_way- сравнивает открытый проект с новой поставкой и с поставкой, от которой обе произошли. Отвечает числом узлов, различающихся и односторонних, а также именами изменённых объектов метаданных на каждой стороне. Только читает: объединение не выполняет и выполнить не может (893907c, 7f0acd6)GET /mcpназывает все обслуживаемые ревизии протокола вprotocol_versions; скалярprotocol_versionсохранён - это ревизия, на которой сходится рукопожатие (3dc5c08)- В релизном workflow появился
workflow_dispatchс тегом на входе: сборку тега можно повторить, не переписывая сам тег (11074b8)
Исправлено
- Запрос проекта-расширения резолвится в конфигурации, которую оно расширяет. Унаследованные поля заимствованного объекта в самом расширении не резолвятся, поэтому смешанный запрос не проверялся нигде; ответ полем
resolvedInProjectназывает модель, которая ответила (7b967cc) - Заявку на информационную базу выдаёт ядро (
FileLock), а не эвристика pid плюс время старта. Замок снимается операционной системой, когда держатель кончается - падением в том числе (0ee8b15) - Конверт
Pendingраспознаётся по метке, которую ставят восемь производителей, вместо догадок по длине ответа и словам в тексте: конверт длиннее четырёх тысяч знаков переставал становиться задачей (a52ce14) - Отказ фоновой работы приходит структурой с
success:falseи типом исключения вместо строкиError:плюс сообщение, которого у большинства исключений нет (b3f2157) - Счёт маркеров считает оба источника, из которых берутся строки: заголовок мог назвать число меньше, чем строк под ним (f28d570)
branch_infobase bindотклоняетapplicationId, которого в проекте нет: опечатка превращалась в постоянный отказ обновления на другой операции (b58ceed)- Ветка перепроверяется на строке перед самим обновлением, а не только в начале вызова (8cbfd33)
- Безмолвное исключение называет кадр, из которого пришло:
NullPointerException at TopObjectInfo.setUuidвместоNullPointerException(e0309aa) - В справке фасадов названы пять операций, которые работали, но упомянуты в ней не были:
branch_infobase,read_event_log,start_client,unpack_external_binary,system_enum_values. Отставание справки теперь ломает сборку (80f103c)
Проверено
- Сборка: 1619 тестов, 0 падений, 0 ошибок. Шесть затворов проходят
- На живой 1C:EDT 2026.2.0.289, конфигурация из 34265 узлов сравнения: одинаковые стороны дают 0 различий; копия с одним изменённым общим модулем даёт 4 различающихся узла и называет
CommonModule._ДемоЗаметки; три стороны считают так же; прогон дольше предела возвращается ключом, сеанс остаётся здоров - Запросы в расширении: заимствованный объект с унаследованным полем и смешанный запрос приходят чистыми, несуществующее поле остаётся ошибкой, обычный проект не затронут
Требования
1C:EDT 2026.1 или 2026.2, Java 17.
Установка
Update site: https://desko77.github.io/ai-edt/
AI-EDT v0.2.31
Четыре новые операции и исправления к ним: права роли на несуществующие объекты, оформление ячеек табличного макета, значения системных перечислений и межпроцессная заявка на информационную базу.
Что нового
Добавлено
audit_role_rights mode=orphans- права роли на объекты, которых в конфигурации больше нет. Решение трёхзначное: удаляется только «точно нет», «не смог определить» перечисляется отдельным списком и не трогается.apply=trueотклоняется, если пресет запрещает запись (22e5551)mxl_workshop operation=format_cells-textPlacement,textOrientation,rowHeight,autoColumnWidth,columnWidth,columnWidthWeightна диапазоне ячеек (22e5551)docs_lookup operation=system_enum_values- значения системного перечисления с обоими именами (name,nameRu); полеvaluesFromназывает тип, из которого прочитано (dba70d8)- Межпроцессную заявку на информационную базу берут все шесть операций, запускающих Конфигуратор:
list_extension,uninstall_extension,unpack_external_binary,export_extension,export_configuration_to_cf,install_extension- прежде толькоupdate_database(e02c638)
Исправлено
mxl_workshop operation=format_cellsбыл недостижим: не внесён в каталог операций и отклонялся до диспетчеризации (29e76a2)audit_role_rights mode=orphansне разрешал дочерние FQN (Subsystem.A.Subsystem.B,DataProcessor.X.Command.Y): 25 записей из 376 у одной роли оставались неразрешёнными (29e76a2)scripts/check-facade-gates.pyне проверял мастерские (29e76a2)
Проверено
- Сборка: 1610 тестов, 0 падений, 0 ошибок. Затворы
check-tool-catalog.py,check-facade-gates.py,check-skill-coverage.pyиtest-coverage-audit.py --checkпроходят - На живой 1C:EDT 2026.2.0+289: значения перечисления приходят и по русскому, и по английскому имени; тип, не являющийся перечислением, отклоняется; несуществующее имя отклоняется без утверждения, что его нет;
format_cellsназывает недостающие свойства;list_extensionработает с новой межпроцессной заявкой
Требования
1C:EDT 2026.1 или 2026.2, Java 17.
Установка
Update site: https://desko77.github.io/ai-edt/
AI-EDT v0.2.30
Выпуск исправлений к 0.2.29: ревизия протокола в ответе, сторож ветки, жизненный цикл задания, заявка на информационную базу и занятие порта.
- Запрос, объявивший в
params._metaревизию 2025-11-25 или старше, получает форму ЭТОЙ ревизии, а не 2026-07-28 initializeбольше не соглашается на 2026-07-28 - ревизию, в которой рукопожатия не существуетProtocolEraразличает версию неверного типа,clientCapabilitiesневерного типа и неизвестную версию; неизвестная версия проверяется раньше отсутствующего поля- Сторож ветки читает привязку проекта, названного вызовом: у расширения она лежит в самом расширении и прежде игнорировалась полностью
- Нечитаемый файл привязок и неопределимая ветка при наличии привязок теперь отклоняют обновление, а не пропускают его
- Задание подписывается на завершение работы: снят захват результата первым опросом, слитые вызовы получают общее задание, заявленный TTL перестал расходиться с реестром
- Результат задания проходит лимит ответа и маскирование персональных данных
- Заявка межпроцессной блокировки публикуется атомарно и освобождается только своим владельцем
- Порт считается свободным только когда свободен адрес, на который ходят клиенты
server/discoverсообщает, кто ответил- У
call_hierarchyнеполный обход теперь называет себя неполным, а лимит тратится на различных вызывающих get_project_errors: баннер переполнения не появляется, когда вернулось всё, что нашлось
Две Critical: правило читалось не из того списка
Форма ответа выбиралась по списку поддерживаемых ревизий. Поле io.modelcontextprotocol/protocolVersion объявляет ревизию, которую использует ЭТОТ запрос. Ответ с resultType и _meta.serverInfo на запрос, назвавший 2025-11-25, - это тело, которого названная ревизия не определяет. Один список отвечал на два разных вопроса: какие ревизии обслуживаются и какие определяют эту форму.
Списка теперь два. SUPPORTED_PROTOCOL_VERSIONS - кого обслуживаем, MODERN_PROTOCOL_VERSIONS - кто получает форму 2026-07-28. Названная старая ревизия обслуживается своей формой, а не отказом: она поддерживается, просто просили не эту. То же разделение убрало вторую нелепость - HANDSHAKE_PROTOCOL_VERSIONS не содержит 2026-07-28, поэтому initialize больше не соглашается на ревизию, в которой сам не существует.
Инвариант совместимости при этом не пострадал и проверен заново: клиент без _meta получает те же 44 инструмента без единого нового поля.
Сторож ветки читал привязку не того проекта. У расширения нет своей информационной базы, поэтому обновление маршрутизируется в конфигурацию, которую оно расширяет, - и сторож читал привязки ЕЁ проекта. Но разработчик работает в расширении, на ветке расширения, и привязку задаёт там же. Её можно было создать, получить подтверждение и не получить никакого эффекта.
Сначала проверяется проект, названный вызовом, затем владелец базы.
Задания: три дефекта из одного решения
Задание забирало результат на том опросе, который первым заставал работу завершённой. Отсюда следовали три разных отказа:
- результат ИЗЫМАЛСЯ, поэтому второе задание над тем же прогоном - а это происходит при каждом слиянии одинаковых вызовов - не находило ничего и сообщало, что прогон исчез;
- два одновременных опроса проходили проверку «ещё не терминальное», и проигравший переписывал ответ победителя отказом;
- реестр удаляет невостребованный результат через пять минут, а задание объявляло тридцать.
Задание теперь подписывается на завершение работы (future.whenComplete), и ответ копируется в него в момент готовности - независимо от того, опрашивает его кто-нибудь или нет. Плюс одно задание на прогон и статус, который во всех переходах пишется последним.
Отдельно: результат задания уходил мимо лимита ответа и мимо маскирования персональных данных. Применимость настройки приватности не может зависеть от того, сколько работал инструмент.
Межпроцессная блокировка
CREATE_NEW атомарен относительно СУЩЕСТВОВАНИЯ файла, но не относительно его содержимого: читатель, пришедший между созданием и записью, видел пустой файл, считал его испорченным, удалял и брал блокировку себе. Заявка теперь пишется во временный файл и переносится Files.move без замены - атомарно и по существованию, и по содержимому.
close() проверяет, что заявка всё ещё своя: последовательность «освободили - взял другой - закрылись повторно» удаляла чужую. Ошибка удаления больше не проглатывается: пока pid жив, такая заявка читается всеми как удерживаемая, и это единственная неудача здесь, требующая человека.
Ключ базы канонизируется (toAbsolutePath().normalize()), а регистр складывается только на файловых системах, которые его складывают: на Linux /data/Base и /data/base - две разные базы.
Порты
Порт считался свободным, если привязался хоть один адрес. При занятом IPv4 и свободном IPv6 это означало, что сервер объявлял порт своим, а 127.0.0.1:порт принадлежал другому процессу - то есть все клиенты попадали не туда. Первый адрес (тот, куда ходят клиенты) обязан привязаться; остальные семейства - по возможности.
Верхняя граница диапазона считается в long и ограничивается 65535, слушатель регистрируется для очистки сразу после create().
Что подтверждено, но не в этом выпуске
Двенадцать находок вынесены отдельной работой, и у каждой названа причина: TOCTOU при удалении устаревшей заявки требует смены модели владения на FileLock; удержание блокировки до конца обновления требует отслеживания состояния на стороне EDT; типизированный контракт Pending затрагивает шесть производителей и собственный реестр YAxUnit; счёт маркеров, включающий Eclipse-маркеры, требует общего дедуплицированного прохода. Полный список - в теле выпуска на GitHub нет места, он в рабочих заметках.
Что отклонено
Сортировка каталога инструментов не могла сломать «прежний» порядок для старого клиента: до неё порядок был порядком обхода хеш-таблицы, то есть нестабильным между вызовами.
Диапазон портов по умолчанию не ломает установки с жёстко прописанным URL клиента: без диапазона сервер при занятом порте не стартовал вовсе, и клиент был неработоспособен в любом случае.
Проверено на стенде на этой сборке
Запрос с 2025-11-25 в _meta - 44 инструмента, ни resultType, ни _meta, ни ttlMs; запрос с 2026-07-28 - полная новая форма; initialize с 2026-07-28 отвечает 2025-11-25, с 2025-06-18 отвечает 2025-06-18; server/discover называет имя, версию и рабочее пространство; неизвестная версия без capabilities даёт -32022 (версия проверяется раньше поля), clientCapabilities неверного типа даёт -32602 с именем поля; клиент без метаинформации - те же 44 инструмента без новых полей.
Сборка: 1591 тест, 0 отказов.
AI-EDT v0.2.29
- Реализована ревизия 2026-07-28 спецификации MCP: метод
server/discover, версия и возможности клиента вparams._metaкаждого запроса, полеresultTypeв каждом результате, расширениеio.modelcontextprotocol/tasksповерх внутреннего механизмаrunKey - Совместимость: ответы клиентам ревизий 2024-11-05 - 2025-11-25 не изменились побайтово - ни
resultType, ни_meta, ни полей кеширования в них не добавлено - Результат
tools/listсодержитttlMsиcacheScope; порядок инструментов детерминирован (сортировка по имени) - Сервер занимает первый свободный TCP-порт из диапазона (по умолчанию 10 подряд от заданного); при занятости всего диапазона ответ перечисляет диапазон и идентифицирует держателя по реестру экземпляров
- Межпроцессная блокировка монопольных операций: при конфликте ответ содержит держателя, выполняемую им операцию и время захвата
- Ветка git связывается с приложением проекта;
update_databaseотклоняет обновление приложения, не связанного с текущей веткой - Порог капа маркеров вычисляется после применения отбора, а не по всему проекту
- Описание проверки резолвится по идентификатору из маркера: покрытие на реальном проекте выросло с 7 из 51 до 31 из 51
- У
call_hierarchyпоявился параметрdepth(до 5 уровней) для направленияcallers
Протокол
Спецификация MCP выпустила ревизию 2026-07-28 с изменённой моделью установления соединения: метод initialize и уведомление notifications/initialized удалены. Клиент передаёт версию протокола и свои возможности в params._meta каждого запроса, обнаружение сервера выполняется методом server/discover, и каждый результат обязан содержать дискриминатор resultType.
Реализация: ревизия определяется на уровне отдельного запроса и только по его содержимому (ProtocolEra). Состояние между запросами не сохраняется, что и позволяет одному эндпоинту обслуживать обе модели соединения одновременно и в произвольном чередовании - конфигурация, которую спецификация называет dual-era server и явно разрешает.
Инвариант совместимости: ответ клиенту ревизии 2025-11-25 и старше не изменился побайтово. Не «остался совместимым», а именно не изменился: без resultType, без _meta, без ttlMs/cacheScope. Проверено на стенде: tools/list legacy-клиента возвращает 44 инструмента и ни одного нового поля.
Кеширование каталога: tools/list - крупнейший единичный ответ сервера (~133 КБ), передаётся до первого вызова инструмента и не меняется до переустановки плагина или смены пресета. Добавлены ttlMs = 300000 и cacheScope = private. Пять минут - компромисс: снятие инструмента пресетом должно вступать в силу за обозримое время, поскольку уведомления toolsListChanged сервер не отправляет. Порядок инструментов ранее определялся итерацией ConcurrentHashMap; теперь список сортируется по имени, что делает ответ байт-идентичным между вызовами.
Расширение задач
Механизм runKey существовал и раньше: инструмент, превысивший мягкий таймаут, возвращает ключ и продолжает работу в фоне. Ограничение было в контракте - чтобы воспользоваться ключом, вызывающий должен знать инструмент, воспроизводящий набор аргументов и то, что результат забирается повторным вызовом того же инструмента.
Расширение io.modelcontextprotocol/tasks вводит durable-идентификатор: CreateTaskResult с resultType: "task", опрос через tasks/get, отмена через tasks/cancel, подтверждение ввода через tasks/update. Реализованы все три метода; статусы - working / completed / failed / cancelled (состояние input_required не используется, поскольку ни одна операция сервера не запрашивает ввод).
Существенная деталь реализации: реестр PendingWorkRegistry отдаёт результат однократно и удаляет запись, тогда как задание опрашивается многократно и его терминальное состояние обязано быть стабильным. Поэтому первый опрос, обнаруживший завершение, извлекает результат из реестра и кеширует его в записи задания до истечения TTL (30 минут).
Задание создаётся только для клиента, объявившего расширение в clientCapabilities. Остальные получают прежний ответ с runKey без изменений.
Два уточнения по спецификации:
Код ошибки. Черновик ext-tasks указывает -32003 для Missing Required Client Capability. Основная схема ревизии 2026-07-28 (schema/2026-07-28/schema.ts) резервирует за протоколом диапазон от -32020 и явно помечает -32002 и -32003 как выведенные из обращения. Клиенты реализуются по основной схеме, поэтому используется -32021.
Область применимости. Раннер YAxUnit ведёт собственный реестр запусков (ACTIVE_LAUNCHES) с ключами вне PendingWorkRegistry. Задание над таким ключом не может быть разрешено ни при каком опросе, поэтому оно не создаётся - проверка выполняется запросом к реестрам, а не списком имён инструментов, так что новый инструмент не может незаметно попасть в ту же ситуацию.
Несколько экземпляров EDT на одной машине
Порт. Ранее каждому экземпляру требовалось задать порт вручную в настройках; при конфликте сервер не стартовал, а диагностика ограничивалась сообщением о невозможности привязки. Реализован диапазон: сервер последовательно пробует порты от заданного, занимает первый свободный и публикует в реестр экземпляров фактически занятый. При занятости всего диапазона сообщение перечисляет диапазон и, по реестру, соседний экземпляр с его портом. Ширина диапазона - преференция mcpServerPortSpan, по умолчанию 10; значение 1 воспроизводит прежнее поведение для конфигураций клиентов с жёстко заданным портом.
Информационная база. Два экземпляра, выполняющие монопольную операцию над одной базой, конфликтуют, но диагностика конфликта отсутствует: возвращается ошибка платформы о заблокированной конфигурации либо ожидание до таймаута. Оба случая неотличимы от отказа инструмента.
Реализована межпроцессная блокировка (MonopolyLock): файловая заявка в ~/.aiedt/locks, ключ - тождество информационной базы, вычисленное из строки соединения (InfobaseIdentity), а не из имени проекта, поскольку имя проекта локально для воркспейса. Ожидание не предусмотрено: при конфликте возвращается отказ с идентификацией держателя, выполняемой операции и времени захвата.
Заявка процесса, завершившегося аварийно, перехватывается по паре pid + время старта процесса - проверка времени старта исключает наследование блокировки переиспользованным pid. Нечитаемая заявка удаляется. Без этих двух правил единичное падение делало бы операцию неисполнимой на машине до ручного удаления файла.
Блокировку берёт update_database. Шесть точек в BmInfobaseExtensionHelper пока не покрыты: у каждой собственный объект результата, модуль реализован через рефлексию, и эти пути не исполняются в headless-прогоне тестов. BmBinaryImportHelper в список не входит - он работает с временной информационной базой и не конкурирует за монополию.
Привязка ветки к информационной базе
Сценарий отказа: переключение ветки меняет метаданные, последующее update_database выполняет реструктуризацию таблиц базы под новую конфигурацию. Операция необратима, а с точки зрения инструмента вызов ничем не отличается от обычного.
Реализовано сопоставление «ветка -> applicationId», хранимое в .settings/aiedt-branch-infobases.yaml проекта (тот же каталог, что у маркеров и кластеров). update_database сверяется с ним и отклоняет обновление, если ветка связана с другим приложением; переопределяется параметром ignoreBranchBinding=true, который входит в состав runKey и потому не может быть удовлетворён закешированным отказом.
Проверка выполняется только при наличии явно заданной связи. Нечитаемый файл настроек интерпретируется как отсутствие связей, а не как основание для отказа.
Автоматического переключения нет: выбор информационной базы за пользователя - решение с той же ценой ошибки, от которой защищает сама проверка.
Текущая ветка читается из .git/HEAD без git-библиотеки. Покрыты тестами нетривиальные случаи: .git как файл со ссылкой gitdir: (worktree, submodule), абсолютный и относительный путь ссылки, имя ветки с несколькими сегментами (feature/tax/report), detached HEAD.
Точность ответов
Кап маркеров. Порог PROJECT_SUMMARY_THRESHOLD сравнивался с общим числом маркеров проекта независимо от применённого отбора. Запрос с checkId, заполнивший страницу, получал сообщение о сорока тысячах маркеров в проекте и рекомендацию передать checkId. Счёт теперь выполняется через тот же предикат, что и выборка строк, с потолком в COMPACT_SCAN_CAP и явным указанием на неточность при его достижении. Рекомендация зависит от того, применён ли отбор.
Резолвинг описаний проверок. Маркер возвращает идентификатор вида module-unused-method, файл описания поставляется как module-unused-method-check.md; оба имени формируются EDT. Порядок кандидатов: точное имя, нижний регистр, затем те же варианты с суффиксом -check. Точное имя обязательно первым - одно описание существует под обоими именами одновременно. Измерено на реальном проекте: из 51 срабатывающей проверки резолвилось 7, стало 31; оставшиеся 20 - legacy- и Xtext-диагностики EDT, не имеющие описаний, о чём теперь сообщается явно. Та же процедура резолвинга используется для колонки Docs в таблице находок, которая ранее возвращала false для существующих описаний.
Область проверки
Проверено на стенде на данной сборке: server/discover (пять поддерживаемых ревизий, объявленное расширение задач, ttlMs/cacheScope); tools/list для legacy-клиента - 44 инструмента без новых полей; tools/list для клиента текущей ревизии - resultType, ttlMs, cacheScope, _meta.serverInfo, сортировка по имени; отказ -32022 на неподдерживаемой версии со списком поддерживаемых; tasks/get на невыданный идентификатор (-32602) и на legacy-клиенте (-32601); call_hierarchy с depth=3 - 11 вызывающих на трёх уровнях; резолвинг описания проверки по идентификатору маркера; кап маркеров в обоих направлениях; branch_infobase через фа...