Skip to content

Releases: Desko77/ai-edt

AI-EDT v0.2.38

Choose a tag to compare

@github-actions github-actions released this 28 Aug 12:39

Квалификатор входит в запрос

Названный квалификатор входит в запрос на применение типа: при 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

Choose a tag to compare

@github-actions github-actions released this 28 Aug 10:11

Отказ приходит текстом

Ответ инструмента с типом ответа 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

Choose a tag to compare

@github-actions github-actions released this 27 Aug 14:52

Реестр поддержки поставщика

Инструмент 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

Choose a tag to compare

@github-actions github-actions released this 20 Aug 08:53

Исправление, которое нельзя выполнить, теперь так и говорит; справка отвечает по одной операции; найден класс параметров, недостижимых через фасад.

Исправление против действия интерфейса

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

Choose a tag to compare

@github-actions github-actions released this 20 Aug 07:52

Обновление на поддержке доведено до сценария, отвечают три базовых метода протокола, поиск больше не требует называть проект.

Решения возвращаются от того, кто их принял

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

Choose a tag to compare

@github-actions github-actions released this 20 Aug 06:18

Сравнение конфигураций тремя сторонами научилось применять принятые решения к проекту.

Объединение по решениям

У 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

Choose a tag to compare

@github-actions github-actions released this 20 Aug 03:42

Сравнение конфигураций тремя сторонами, валидация запросов в расширениях и семь исправлений в том, как сервер сообщает о происходящем.

Что нового

Добавлено

  • 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

Choose a tag to compare

@github-actions github-actions released this 19 Aug 17:39

Четыре новые операции и исправления к ним: права роли на несуществующие объекты, оформление ячеек табличного макета, значения системных перечислений и межпроцессная заявка на информационную базу.

Что нового

Добавлено

  • 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

Choose a tag to compare

@github-actions github-actions released this 19 Aug 12:24

Выпуск исправлений к 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

Choose a tag to compare

@github-actions github-actions released this 19 Aug 10:36
  • Реализована ревизия 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 через фа...

Read more