ADR-024 — Обмен процессами: подключаемые конвертеры импорта/экспорта (BPMN 2.0 первым)¶
| Поле | Значение |
|---|---|
| Статус | Принято |
| Версия | v.7 |
| Дата | 2026-08-28 |
| Владелец | Руслан Габитов |
| Уточняет | SAD-001 v.1.3 §4 N7 / §5 / §9 / §14, ADR-002 v.2, ADR-019 v.1, ADR-003 v.2 |
EN-оригинал — канонический: ADR-024-process-interchange-converters.md. Этот файл — его перевод (twin). При расхождении приоритет у английского текста.
Этот ADR решает, как определение процесса пересекает границу между внешним форматом обмена и моделью gobpm в памяти, в обе стороны, не связывая ни то ни другое с движком: шов конвертеров, не зависящий от формата, в ядре, реестр регистрации по ключу формата за ним и конвертер BPMN 2.0 XML «в комплекте» рядом. Он фиксирует, что обязан покрыть импорт — весь набор элементов, которые движок выполняет, — политику для трёх подключаемых языков, которые может нести определение, и что происходит с каждой конструкцией вне этого набора.
1. Контекст и проблема¶
gobpm строит определение процесса программно: конструктор на элемент, Add,
чтобы разместить, Link, чтобы соединить. Это вся поверхность авторинга, и она
блокирует внедрение: люди, которые моделируют процессы, пользуются
инструментами моделирования, а те производят файл .bpmn.
Управляющий SAD называет этот пробел в трёх местах:
- SAD-001 v.1.3 §4 N7: «Парсер BPMN XML … Парсер будет существовать (обязан, ради внедрения), но это отдельная забота, конструирующая модель в памяти, которую потребляет движок. Основная библиотека принимает уже построенные модели.»
- §5: стейкхолдер BPMN-моделировщик авторит «BPMN 2.0 XML для исполнения goBpm» и нуждается в «строгом соответствии спецификации; внятной обратной связи о неподдерживаемых элементах».
- §14: у Process Execution Conformance два требования, и второе — §2.3.2, импорт Process-диаграмм — принадлежит конвертеру, а не движку.
Две силы за пределами SAD формируют решение. Требование двунаправленное — прочитать файл в модель и записать модель обратно, — поэтому шов обязан быть симметричным там, где N7 говорит лишь о парсере. И он обязан быть подключаемым по форматам: BPMN 2.0 XML — это то, что поставляется, но host или третья сторона должны иметь возможность поставить за тот же контракт XPDL, JSON-DSL для процессов или вендорский диалект — что и есть философия расширений движка в целом (ADR-002 v.2).
Более трудная половина задачи — забор. Конвертер, читающий часть того, что движок выполняет, стоит гораздо меньше, чем подсказывает доля, потому что определение — это граф: импортёр, понимающий восемь из девяти видов элементов файла, не импортирует вообще ничего, поскольку девятый обрывает документ. Значит вопрос не в том, какие элементы отобразить первыми, а в том, где импорт обязан оказаться в итоге, и ответ должен быть сформулирован как решение, а не нащупан по одному элементу за раз.
2. Решение¶
2.1 Шов конвертеров в ядре, не зависящий от формата¶
Шов — это два интерфейса и реестр, в пакете ядра pkg/convert. Никакого XML,
никакой специфики формата, только stdlib — так что ядро сохраняет свой бюджет
зависимостей «stdlib + uuid»
(SAD-001 v.1.3 §9.1).
// package convert
// Format идентифицирует формат обмена в реестре.
type Format string
const BPMN Format = "bpmn"
// Importer строит определение процесса в памяти из внешнего представления,
// прочитанного из r.
type Importer interface {
Import(ctx context.Context, r io.Reader) (*process.Process, error)
}
// Exporter сериализует определение процесса из памяти в w.
type Exporter interface {
Export(ctx context.Context, w io.Writer, p *process.Process) error
}
Интерфейсы разделены, а не объединены: формат может поддерживать лишь одно направление, и они регистрируются независимо.
2.2 Реестр с регистрацией по ключу формата¶
Реестр — это собственный кодековый паттерн Go image.RegisterFormat /
image.Decode: пакетные map'ы, ключёванные Format, наполняемые в init()
каждого пакета-конвертера, с тонким фасадом над ними: регистрация и снятие
регистрации по направлениям, Import/Export с диспетчеризацией по ключу и
Formats() для диагностики. Поскольку init() некуда вернуть ошибку, у
регистрации есть init-двойник, который записывает плохую регистрацию и выносит её
при первом использовании, а не паникует при загрузке.
Это намеренное отклонение от нормы функциональных опций ADR-002, по которой
расширение передаётся в конструктор движка. Глобальный для пакета реестр нужен
именно потому, что convert не зависит от движка (§2.4): нет никакого
thresher.New, на который можно повесить опцию WithConverter, а таблица
кодеков — это ключёванное состояние, общее для всех вызывающих, а не конфигурация
на движок.
Регистрация валидирует каждый аргумент на публичной границе: пустой Format,
nil-реализация и повторная регистрация той же пары (формат, направление) —
все отвергаются самоидентифицирующей ошибкой, называющей функцию и провинившийся
аргумент. Import/Export на незарегистрированном формате называют, что
зарегистрировано, — так что вызывающий, забывший blank import, видит причину, а
не голый отказ.
Формат — явный аргумент; шов не нюхает содержимое. Детектирование
инспекцией может промахнуться на вендорском диалекте или на BOM, а вызывающий
всегда знает, что держит в руках. Сниффер Detect/ImportAny аддитивен и может
появиться, если второй формат когда-нибудь сделает его полезным.
2.3 BPMN-конвертер — отдельный пакет «в комплекте»¶
Конвертер BPMN 2.0 XML — это pkg/convert/bpmn, пакет-брат шва. Он импортирует
ядро (шов — ради интерфейсов, pkg/model/* — чтобы строить и читать модель),
держит весь код encoding/xml и саморегистрирует оба направления в своём
init().
«В комплекте» означает first-party, в репозитории, нулевая конфигурация после
blank import, по модели image/image/png: blank import регистрирует формат, и
convert.Import(ctx, convert.BPMN, r) после этого работает. Пользователь ядра,
который никогда его не импортирует, получает чистую ошибку «неизвестный формат», а
не скрытую XML-зависимость. Примеры и серверный слой делают blank import, так что
опыт «из коробки» — BPMN-готовый.
Два инварианта держат это на месте. Ядро никогда не импортирует конвертер —
зависимость идёт в одну сторону, от пакета формата к модели, что и означает на
практике «ядро принимает уже построенные модели» из N7; это обеспечивается
механически правилом depguard, а не границей модуля. И шов остаётся не
зависящим от формата: ничто в pkg/convert не знает, что такое XML.
Сделать BPMN настоящим умолчанием ядра — без blank import — здесь не решается. Это положило бы формат в ядро и потому требует ревизии SAD-001 N7, а это решение SAD; blank import стоит host'у одной строки и тем временем сохраняет ядро чистым.
2.4 Никакой связанности с движком; результат регистрирует host¶
Импорт возвращает *process.Process и на этом останавливается. Host сам
регистрирует его в движке, поэтому convert пригоден вообще без движка —
офлайн-валидация, инструментарий, тесты, — а движок не несёт зависимости от
конвертера ни в одну сторону.
Там, где работающему сервису нужно «загрузить .bpmn и зарегистрировать», эта
композиция принадлежит серверному слою, который импортирует и ядро, и
конвертер. Удобство thresher.ImportAndRegister(format, r) потому вне области
рассмотрения: оно развернуло бы зависимость ради экономии в две строки, а место,
которому это действительно нужно, — продуктовая забота со своим потоком работ.
2.5 Импортированная идентичность питает версионирование¶
BPMN-id элемента <bpmn:process> и каждого flow-элемента сохраняется как
foundation-идентичность модели. Это несущее:
ADR-019 v.1 делает ключом версии id
процесса — «Две регистрации, несущие один id, — это две версии одного
логического определения», — поэтому импортёр, чеканящий свежие авто-id, сделал бы
каждый импорт одиночной первой версией и тихо победил бы версионирование. Экспорт
пишет идентичность обратно, так что зарегистрированное определение проходит
round-trip. Отсутствующий или пустой id — ошибка импорта, а не тихий авто-id:
стандарт требует id на flow-элементах, а выдумывание его отбрасывает
собственное именование моделировщика.
2.6 Подмножество элементов экспорта¶
Экспорт покрывает исполняемое ядро: definitions и process; none-старты и
none-концы; абстрактную задачу, ручную задачу, пользовательскую задачу и
сервисную задачу с её каталогом операций; sequence flow с условиями; и
исключающий и параллельный шлюзы.
| BPMN XML | Цель в модели | Обоснование |
|---|---|---|
<bpmn:definitions> (корень) |
конверт документа | §10 |
<bpmn:process> |
процесс, несущий импортированный id | §10 |
<bpmn:startEvent> (none) |
none start event | §13.5.1 |
<bpmn:endEvent> (none) |
none end event | §13.5.6 |
<bpmn:task> / <bpmn:manualTask> |
ручная задача (неоперациональная, §13.1) | semantics/tasks.md |
<bpmn:userTask> |
пользовательская задача | semantics/tasks.md |
<bpmn:serviceTask> (operationRef) |
сервисная задача | semantics/tasks.md |
<bpmn:interface> / <bpmn:operation> |
каталог операций | elements/service-interfaces.md |
<bpmn:sequenceFlow> (sourceRef/targetRef, conditionExpression) |
связь, условная там, где объявлено | §13.2 |
<bpmn:exclusiveGateway> (default) |
исключающий шлюз | §13.4.2 |
<bpmn:parallelGateway> |
параллельный шлюз | §13.4.1 |
Обмен диаграммами — bpmndi:*, dc:*, di:* — опускается на экспорте и
терпится на импорте: это «не часть execution conformance»
(conformance.md), и движку нечего писать
обратно.
Отставание экспорта от импорта — реальная асимметрия, и §5 фиксирует её цену; §7 фиксирует, что её закрытие — ближайшая последующая работа, а не открытое будущее.
2.7 Пространства имён и обратная связь о неподдерживаемых элементах¶
Импортёр сопоставляет по URI пространства имён плюс локальному имени, никогда
по строке префикса: файл может привязать модельное пространство имён BPMN к любому
префиксу, а конвертер, ключёванный на bpmn:, читает только те файлы, которые
довелось видеть его автору. Элемент в пространстве имён BPMN, который конвертер не
отображает, отвергается с тремя фактами, нужными моделировщику: тег, id
элемента и раздел спецификации, который его определяет, — что и есть
требование обратной связи SAD-001 §5 в конкретной форме. Чужие пространства имён
вне области исполнения — предмет §2.14.
2.8 Round-trip семантический, а не побайтовый¶
Round-trip по элементам, которые покрывают оба направления, даёт процесс, структурно и семантически эквивалентный (те же узлы, id, потоки, условия и виды шлюзов), и не побайтово идентичный XML. Форматирование, порядок атрибутов, отброшенный обмен диаграммами и нормализация префиксов пространств имён законно отличаются.
Это решение движка: стандарт не требует текстового round-trip без потерь для Process Execution Conformance, а round-trip уровня моделировщика — это другой продукт, нежели движок исполнения.
2.9 Импорт покрывает весь набор элементов execution conformance¶
Импорт отображает каждый элемент, который движок выполняет — список «In scope» из conformance.md, перечисляющий то, что анимирует Clause 13, плюс потребляемые им поддерживающие классы. Внутри этого списка нет подмножества импорта: файл, который движок мог бы выполнить, — это файл, который конвертер умеет прочитать.
Всё вне этого списка несёт явную диспозицию, потому что «не отображено» и «не присутствует» не должны выглядеть одинаково:
| Семейство | Диспозиция на импорте | Почему |
|---|---|---|
| Lane / LaneSet | разобрать и сохранить, поведения не навешивать | model-only по conformance.md; §2.3.1 разрешает исполнению игнорировать дорожки, §2.3.2 обязывает импортёр их сохранить |
Артефакты — textAnnotation, group, простая association |
разобрать и сохранить в model-only ярус артефактов, поведения не навешивать | ADR-039 v.1: то же прочтение двух обязательств, что и у дорожек, — загрузка §2.3.2 и round-trip §2.8 требуют, чтобы модель держала то, что утверждает диаграмма, тогда как исполнение это игнорирует. Ссылка, которую модель не может разрешить, деградирует именно этот артефакт до отчёта §2.14 — файл выживает |
category / categoryValue |
потребить как вход разрешения во время загрузки — значение, которое встраивает группа; элемент модели не создаётся | ADR-039 v.1 §2.3 |
association (компенсационная форма) |
отобразить — разрешается в обвязку обработчика boundary-события, а не дублируется как артефакт | семантика исполнения (§8.4.1); один факт документа — одно представление в модели (ADR-039 v.1 §2.4) |
Обмен диаграммами (bpmndi: / dc: / di:) |
тихо пропустить | не часть execution conformance |
relationship |
тихо пропустить | не связано с исполнением |
import |
отобразить — собирается по пространству имён и привязывается к тому <itemDefinition>, чей префикс structureRef в него разрешается; тот, на который никто не ссылается, попадает в отчёт |
import объявляет, где живёт ссылаемый тип, а это осмысленно ровно тогда, когда item definition несёт ссылку на тип |
Семейство GlobalTask |
отобразить — каждый становится процессом в наборе документа (§2.13) | переиспользование по ссылке, обслуживаемое реестром как процесс (ADR-023 v.5 §2.7) |
| Семейства Choreography / Conversation | отвергнуть | отдельные подклассы соответствия — хореография не является процессом, и тихо её отбросить означало бы импортировать не ту диаграмму, которую нарисовал моделировщик |
| Семейство Collaboration | только определительно — см. §2.15 | §2.3.2 называет «определительную Collaboration» частью обязательства импорта |
Правило, решающее каждую строку, — представление против семантики: элемент, на который движок не будет действовать, можно пропустить только тогда, когда его отбрасывание оставляет импортированное определение означающим то же самое. Хореография не проходит этот тест напрочь. Текстовая аннотация его проходит и всё равно переносится, потому что обязательство загрузки §2.3.2 пересекает этот тест так же, как и для дорожек: модель держит то, что утверждает диаграмма.
2.10 Выражения: один поддерживаемый язык, один транслируемый диалект¶
Определение несёт выражения в условиях, таймерах, кардинальности multi-instance, условиях завершения, извлечении корреляции и присваиваниях данных. Стандарт делает язык атрибутом на выражение поверх умолчания уровня документа и ничего не говорит о том, какие языки инструмент обязан реализовать. Так что выбор целиком за движком:
| Язык на проводе | Поведение импорта |
|---|---|
| собственный текстовый язык движка | проброс — несётся как текстовый вид выражения, вычисляется маршрутизированным движком |
JUEL (${…} / #{…}, идиома Camunda) |
транслируется в текстовый язык движка, из исходника в исходник |
| FEEL | отвергается, классифицированно, с именованием языка |
| XPath (в том числе через умолчание схемы) | отвергается, классифицированно |
| что-либо ещё | отвергается, классифицированно |
Почему транслировать JUEL, а не выполнять его. Движок и так маршрутизирует
выражения по заявленному языку, а его собственный текстовый язык — это
C-подобная инфиксная грамматика над той же моделью данных, к которой адресуется
JUEL: сравнение, булева композиция, доступ к членам и по индексу, небольшой набор
встроенных функций. Пересечение достаточно велико, чтобы трансляция была
переписыванием синтаксиса, а не интерпретатором: снятие разделителей,
&&/||/! в их словесные формы и идиомы доступа к переменным.
| JUEL на проводе | Транслировано | Примечание |
|---|---|---|
${total > 100 && tier == "gold"} |
total > 100 and tier == "gold" |
разделители сняты, && → and |
${execution.getVariable("total") > 0} |
total > 0 |
идиома доступа к переменной схлопывается до имени |
${myBean.check(order)} |
— | отвергается по имени: у вызова бина host'а нет соответствия |
Добавление второго вычислителя для языка, чью семантику движок тогда стал бы
владеть — приведения типов, обработка null, разрешение свойств бинов, — не
покупает ничего, чего не даёт переписывание, и стоит вечной второй реализации
каждой семантики выражений, которая у движка есть.
Трансляция падает громко, никогда частично. JUEL-конструкция без соответствия — всё, что тянется наружу за собственные данные процесса, — отвергается по имени. Транслятор, тихо отбрасывающий то, чего не может выразить, производит условие, которое разбирается, вычисляется и уводит токен не туда; отказ тогда всплывает как неверно исполненный процесс, далеко от импорта. Отказ на импорте — единственное место, где ошибка ещё дёшева.
Синтаксическая примета решающая, и объявленный язык её не перебивает. У
expressionLanguage есть схемное умолчание XPath, а XPath отвергается — так что
буквальное прочтение отвергает каждый документ, опустивший атрибут, то есть почти
все, включая файлы, чьи выражения откровенно суть ${…}. Хуже того,
Camunda-файл часто его не опускает: инструмент выписывает схемное умолчание явно,
поэтому документ объявляет XPath, а потом пишет JUEL в каждом условии. Уважить
объявление здесь означает отвергнуть файл на его собственной неверной маркировке,
что не служит никому: разделители — то, что автор действительно написал, а
объявление — то, что инструмент выдал от его имени.
Поэтому язык разрешается сначала по телу:
| Тело выражения | Язык |
|---|---|
несёт ${…} / #{…} |
JUEL, что бы ни объявляли документ или выражение |
| что-либо ещё | собственный language выражения, иначе expressionLanguage документа, иначе отвергается |
Объявление решает только то тело, которого не решают разделители. Это решение движка, зафиксированное в §3, и это то же суждение, что и неуважение схемного умолчания, — применённое к случаю, когда умолчание было выписано, а не оставлено неявным.
2.11 Скрипты: только самодостаточный исходник¶
ScriptTask несёт подсказку scriptFormat и свой исходник; стандарт не
предписывает никакого языка скриптов. Движок маршрутизирует скрипты по этому
формату через свой шов скриптовых движков.
Скрипт импортируется, только когда его тело — самодостаточный исходник: файл
несёт поведение, а движок его интерпретирует. Тело, которое вместо этого несёт
имя чего-то, зарегистрированного host'ом, не импортируется, и различие не в
ссылке-по-имени как таковой: calledElement из §2.13 — тоже имя, которое
разрешает реестр. Дело в том, что именуется. Вызываемый процесс — это
определение, которое стандарт позволяет документу объявить или импортировать; а
Go-функция, зарегистрированная host'ом, — символ, который ни один BPMN-документ
нести не может, поэтому файл, её именующий, вообще не является описанием
поведения. Принять его значило бы допустить процесс, чей скрипт ничего не делает,
пока некая посторонняя программа не зарегистрирует подходящий ключ, и упасть на
исполнении далеко от импорта, который его пропустил.
Отсутствующий scriptFormat отвергается так же, как немаршрутизируемый: когда
маршрутизировать не по чему, альтернатива отказу — гадание, а угаданный язык
прогоняет чужой синтаксис через неверный интерпретатор. Отказ является
отсрочкой, а не приговором файлу: та же скриптовая задача импортируется в тот
момент, когда host зарегистрирует движок, заявляющий этот формат, — ради чего шов
и существует.
2.12 Бизнес-правила: непрозрачная ссылка на решение, никогда не DMN-парсер¶
BusinessRuleTask в стандарте несёт только implementation — атрибута
decisionRef нет, и спецификация не предписывает привязки к движку правил,
отмечая лишь, что типовая обвязка — DMN. Вечная не-цель gobpm состоит в том, что
он никогда не встроит DMN-движок
(SAD-001 v.1.3 N2); его задача
бизнес-правила держит непрозрачную ссылку, которую разрешает настроенный
host'ом движок правил.
Импорт следует этому в точности: он поднимает ссылку на решение — которая, не имея
дома в наборе атрибутов стандарта, приходит через вендорский диалект (§2.14) или
implementation — в непрозрачную ссылку и не разбирает никакого DMN. Задача
бизнес-правила, чью ссылку на решение найти не удалось, отвергается, а не
импортируется инертной, потому что задача правила без решения упадёт на первом же
исполнении, имея гораздо меньше контекста, чем сейчас есть у импортёра.
Что означает здесь «DMN поддерживается», поэтому точно: ссылка проходит round-trip, а решает движок. Решение, которое поставляемый табличный движок выразить не может, — не забота конвертера.
2.13 Global task'и импортируются как callable-процессы; квалифицированный calledElement несёт своё пространство имён¶
Семейство GlobalTask (globalTask, globalUserTask, globalManualTask,
globalScriptTask, globalBusinessRuleTask) — это переиспользование по
ссылке: задача, определённая один раз на уровне definitions и вызываемая через
CallActivity. Движок обслуживает эту ссылку тем же реестром, что обслуживает
вызываемый процесс — ADR-023 v.5
§2.7 решает, что global task является callable-процессом, телом которого
служит эта одна задача, — поэтому обязанность конвертера в том, чтобы произвести
этот процесс, а не разрешать что-либо.
Каждый global task импортируется как ещё один процесс в наборе документа
(§2.15): его id и имя — это id и имя global task'а, его объявленный контракт —
ioSpecification global task'а (носитель уровня процесса из
ADR-040 v.2), а его тело — None Start Event,
задача и None End Event, соединённые двумя sequence flow. Задача строится тем же
прочтением, что и её внутрипроцессный аналог, поэтому семейство не добавляет
второго отображения ни одной конструкции и наследует без изменений всё, что этот
аналог отвергает. ioSpecification callable становится объявленным контрактом
процесса и не копируется на задачу внутри него: параметры задачи наполняются
data associations, callable их не объявляет, а то, что контракт читает при
завершении, — это корневая область, куда и приземляется собственная работа задачи.
<ioSpecification> элемента <callActivity> импортируется как параметры,
переносящие данные через границу вызова, — прямое отображение, без data
associations. Список вложенности §10.4.1 называет только Tasks и CallableElements
и потому читается как исключающий Call Activity, но собственная строка CallActivity
в §10.4 отображает её DataInputs/DataOutputs на таковые callable, что предполагает,
что они у неё есть. При строгом прочтении эта строка недостижима и ни один
импортированный документ не смог бы вообще передать данные в callable, поэтому
«Tasks» читается как активности, которые делают работу. Элементы, синтезируемые
внутри процесса, получают id, производные от id global task'а, а документ, уже
использующий один из них, отвергается за дубликат, а не тихо перекоммутируется.
callActivity, называющий global task того же документа, не требует ничего
сверх: его calledElement — это ключ процесса, который обслуживает реестр.
calledElement читается как QName — решение движка. Стандарт типизирует его
простым String и ничего не говорит о префиксе
(ADR-023 v.5 §2.7 несёт
обоснование), поэтому прочтение его — решение конвертера, принятое потому, что
префикс — это то, что моделировщики пишут, когда callable живёт в другом
документе, и потому, что альтернатива — трактовать ns:P как ключ, содержащий
двоеточие, — может вызвать только не то или ничего.
calledElement |
Диспозиция |
|---|---|
| без префикса | ключ, дословно |
префикс привязан к собственному targetNamespace документа |
локальная часть — ключ; квалификация была самоссылкой |
префикс привязан к пространству имён, объявленному через <import> |
локальная часть — ключ, а пространство имён едет с ней как called namespace call activity; резолвер движка отображает пару во время вызова |
префикс не привязан ни к какому объявленному пространству имён либо к тому, которое не объявляет ни один <import> |
отвергается с именованием префикса — файл некорректен либо ссылается на документ, который никогда не импортировал |
Конвертер разрешает префикс, но никогда callable: на какую регистрацию отображается чужое пространство имён — решение host'а через шов движка, а конвертер несёт пространство имён, чтобы host мог его принять. Импорт не обращается ни к какому реестру, и call activity импортируется до того, как её callable зарегистрирован.
2.14 Распознаваемый вендорский диалект, отображаемый там, где у модели есть дом¶
Молча пропускать каждое чужое пространство имён правильно для разметки и неправильно для конфигурации: assignee в Camunda-файле, его топик внешней задачи и его ссылка на решение — не декорация, это исполняемое содержимое, и у движка есть дом в модели для каждого. Два собственных решения этого ADR недостижимы без диалекта: у ссылки на решение (§2.12) и, на практике, у JUEL (§2.10) вообще нет носителя в стандартном BPMN.
Поэтому конвертер распознаёт один диалект (Camunda 7) и отображает каждую конструкцию, у которой есть дом в модели движка. Три правила его ограничивают:
- Отображать только то, что уже существует. Диалект никогда не мотивирует новый тип модели. Конструкция без дома не отображается, и модель не гнётся, чтобы её принять.
- Никогда молча не отбрасывать распознанную конструкцию. Всё, что находится в распознанном пространстве имён и не отображено, попадает в отчёт (§2.15) — вендорская половина требования обратной связи SAD-001 §5, которой ошибка на flow-элементе служила всегда, а содержимое расширений — никогда. Конструкции, чьё значение принадлежит движку host'а, а не процессу, — подсказки исполнителю задач и границам транзакций, классы слушателей, управление историей — по правилу 1 не отображаются, а по этому правилу попадают в отчёт: они описывают, как другой движок планирует работу, и ответ gobpm в том, что он планирует иначе, а не в том, что он притворится подчиняющимся.
- Нераспознанное пространство имён остаётся в тишине. Конвертер не может отчитываться о словаре, которого не знает, а притворяться иначе значило бы превращать каждую чужую аннотацию в шум.
2.15 Одна опциональная способность шва: документ даёт набор и отчёт¶
Два факта не помещаются в Import(ctx, r) (*process.Process, error).
Документ несёт больше одного процесса. §2.3.2 обязывает импортёр поддерживать Process-диаграммы «включая их определительную Collaboration», а collaboration привязывает участников к нескольким процессам. У gobpm нет типа Collaboration и он не нужен — собственный межпроцессный механизм стандарта — это события-сообщения, которые движок выполняет, а каждый процесс-участник регистрируется и версионируется сам по себе. Но одно возвращаемое значение не может их унести.
Конструкции отбрасываются намеренно. §2.14 требует отчитываться о том, что нёс распознанный диалект и чего модель не держит, а сигнатуре негде это сказать.
На оба отвечает одна опциональная способность рядом с существующими
интерфейсами — Go-идиома capability-интерфейса, который фасад проверяет и без
которого откатывается, как io.ReaderFrom относится к io.Reader:
// package convert
type Result struct {
Processes []*process.Process // в порядке документа
Dropped []Dropped // каждая распознанная неотображённая конструкция
}
type DocumentImporter interface {
ImportDocument(ctx context.Context, r io.Reader) (*Result, error)
}
Import сохраняет своё значение и свою сигнатуру — тот самый процесс
документа — и становится точным в неоднозначных случаях: документ ровно с одним
исполняемым процессом возвращает его; документ без них или с несколькими — ошибка,
называющая, что найдено, и указывающая на вызов уровня документа. Ничто
существующее не ломается, ни один формат не принуждается реализовывать эту
способность, а обязательство по collaboration выполнено без моделирования
Collaboration.
Collaboration потребляется, а не представляется. <collaboration> читается
только ради определительного содержимого — какие участники существуют и на какой
процесс каждый ссылается, — а <messageFlow> попадает в отчёт как отброшенный:
это рисунок обмена сообщениями, чьё исполнение движок производит через события
сообщений и корреляцию. Граф, который выполняет движок, от его присутствия не
меняется.
2.16 Вне покрытия: три класса и конвертер, который никогда не компенсирует¶
Каждая конструкция, которую импортёр не отображает, относится ровно к одному из
трёх классов, а классифицируемая единица — конструкция, а не тег:
<association> держит и отображаемую компенсационную связь, и сохраняемую
простую, а <transaction> импортируется, тогда как одно из значений её атрибутов
— нет.
| Класс | Что это | Что говорит отказ |
|---|---|---|
| Отложенное (staged) | отображаемая работа, до которой ещё не дошли | ничего — это не граница, и план, который её планирует, — единственная её запись |
| Заблокированное способностью | исполнимо, выразимо в документе, заблокировано способностью, которой нет у pkg/model |
называет способность и issue, её отслеживающий, потому что это имя и есть спецификация работы, которая её снимет, — и называет программный маршрут, доступный тем временем |
| Постоянное (standing) | движок это не примет, и не из-за нехватки работы | называет причину и что делать вместо этого, и никогда не говорит «пока» |
Постоянная граница — это либо конструктор, принимающий Go-значение, которое не
может нести ни один документ (счётчики токенов на поток у комплексного шлюза
против выражения activationCondition, поставляемый host'ом Router ad-hoc
контейнера (ADR-035 v.1 §2.2)), либо
решённая не-цель, такая как второй набор входов/выходов на направление. Это не
дефект и никогда не переоформляется в него.
Регистр строк, заблокированных способностями, живёт вместе с эпиком импорта, а не в этом документе: запись решения, накапливающая реестр, перестаёт быть записью решения, и реестр тогда приходится bump'ать на каждом приземлении, тогда как правило, которое он окружает, не двигается.
Классы ограничены двумя правилами:
- Конвертер никогда не компенсирует отсутствующую способность модели. Он
отчитывается и отказывает; он не отращивает приватного парсера, роутера, типа
или второй копии правила модели. Две реализации одного правила расходятся, и
тогда конвертерская побеждает на импорте, а модельная — во время выполнения:
худший возможный раскол, который слою модели пришлось бы позже вытеснять,
сохраняя поведение конвертера нетронутым. Сначала правило режет в другую
сторону: прежде чем объявлять способность отсутствующей, поищи собственный
вход модели и решение, которое этим уже управляет. Таймер доходит до
движка целиком через ISO 8601-конструкторы модели;
methodтранзакции читается и переносится самой моделью, а решает, есть ли у движка координатор для него, регистрация, а не конвертер (ADR-028 v.2 §2.7). Способность, которую не удаётся назвать точно, — обычно та, которую не искали. - Способность приземляется раньше строки, которая её потребляет. Точка
расширения — это изменение модели с обязанностями изменения модели: собственная
запись решения там, где оно меняет контракт, собственный документ приземления, —
а строка конвертера, её потребляющая, — однострочный follow-up, но никогда не
средство доставки самой способности.
ADR-039 v.1 — образец: сначала приземлился
ярус артефактов, строки
<association>последовали.
Читатель, который не может отличить «пока нет» от «никогда», либо ждёт того, что не придёт, либо пересобирает то, что уже правильно, — вот почему три класса различаются формулировкой, а не только исходом.
3. Grounding по стандарту¶
Все утверждения ссылаются на вендоренную KB (docs/bpmn-spec/), которая несёт §-ссылки OMG.
- Цель соответствия. Process Execution Conformance — это §2.3, с двумя требованиями, адресованными «инструменту»: §2.3.1 семантика исполнения и §2.3.2 импорт Process-диаграмм — «Инструмент, заявляющий тип Process Execution Conformance, ОБЯЗАН поддерживать импорт типов BPMN Process-диаграмм, включая их определительную Collaboration». Это второе требование и есть причина существования этого ADR (conformance.md, SAD-001 v.1.3 §14).
- Область элементов импорта. Набор, к которому применяется §2.3.2, — это in-scope-список conformance.md: операциональные элементы Clause 13 плюс потребляемые ими поддерживающие классы. §2.9 принимает этот список дословно, а не его подмножество.
- Вложенность
definitions/process.Process— ребёнокrootElementsуdefinitions; flow-элементы — детиflowElementsуprocess(elements/foundation.md, elements/process.md).isExecutable— атрибут 0..1: требование «исполняемости» — утверждение о соответствии, а не кардинальность схемы. - None-старт/конец. Start или end event с нулём
eventDefinitions— это none-вариант; none-старт «запускает новый экземпляр Процесса» (§13.5.1), none-конец «просто потребляет токен» (§13.5.6) (semantics/events.md, semantics/end-events.md). - Задачи. Абстрактная
taskиmanualTaskнеоперациональны — движок «МОЖЕТ трактовать её как сквозной no-op» (§13.1);serviceTaskразрешаетoperationRef, аimplementation— строковая подсказка (semantics/tasks.md, чью семантику исполнения задач извлечение возводит к §13.3). - Sequence flow.
sourceRef/targetRef— ID-ссылки;conditionExpression— дочерний элемент,Expression(elements/flows.md).isImmediateнеоперационален и МОЖЕТ игнорироваться (semantics/token-flow.md:18). - Шлюзы. Исключающий: «первое условие, вычислившееся в true … иначе default
sequence flow … если все false И нет default → движок бросает» (§13.4.2);
параллельный берёт по токену с каждого входящего и кладёт по одному на каждый
исходящий, «не может бросить» (§13.4.1), и не имеет атрибута
default(semantics/gateways.md). - Дорожки — представление, а не семантика. §2.3.1 разрешает игнорировать неоперациональные элементы во время выполнения; она не разрешает выбрасывать их из модели, а §2.3.2 обязывает импортёр поддерживать диаграмму моделировщика.
- Визуальные артефакты не несут ничего исполняемого.
TextAnnotation,GroupиCategoryперечислены как «чисто визуальные», тогда какAssociationявно сохранён, «потому что несёт компенсационную семантику» (conformance.md) — это и есть линия, которую §2.9 проводит между сохранением и отображением. - DI/DC вне области. «BPMNShape, BPMNEdge … все
bpmndi:*иdc:*,di:*| Метамодель визуальной разметки; не часть execution conformance» (conformance.md:168). - Язык выражений — на выражение поверх умолчания документа.
FormalExpression.language— 0..1, аDefinitions.expressionLanguage— 0..1 с умолчаниемhttp://www.w3.org/1999/XPath(elements/foundation.md). Стандарт нигде не требует от инструмента реализовать какой-то конкретный. - Язык скриптов не предписан.
ScriptTask.scriptFormat— 0..1 (elements/activities.md), и «спека не предписывает язык скриптов» (semantics/tasks.md). - Бизнес-правила не несут ссылки на решение. Единственное собственное свойство
BusinessRuleTask—implementation, и «спека не предписывает привязку к движку правил. Типовая обвязка — к DMN».decisionRefпоэтому является вендорским словарём по построению, а не по нашему упущению — вот почему §2.12 зависит от §2.14. - Global task'и — переиспользование по ссылке. Семейство наследует от
CallableElement, вызывается черезCallActivity.calledElementи несётname,ioSpecificationи никакого собственного потока (elements/activities.md) — что и позволяет §2.13 импортировать каждый как процесс, который реестр обслуживает как любой другой. - Collaboration вне области исполнения библиотеки и внутри обязательства импортёра. «Не анимируется Clause 13; межпроцессный обмен сообщениями покрывается событиями Message. Заметьте, §2.3.2 называет "определительную Collaboration" для импорта — это забота сервера/конвертера» (conformance.md). §2.15 принимает обе половины этой фразы буквально.
Заметки движка (намеренные расхождения). Только семантический round-trip
(§2.8); BPMN-id, трактуемый как долговечная версионная идентичность (§2.5 —
стандарт молчит о версионировании в реестре); неотображённый элемент внутри
пространства имён — жёсткая ошибка импорта, а не мягкий пропуск (§2.7 — строже,
чем требует стандарт, чтобы служить потребности обратной связи §5); объявленный
язык выражений — включая схемное умолчание expressionLanguage — не перебивает
разделители ${…} (§2.10); отвергнутый формат скрипта и отвергнутый язык
выражений — решения движка, поскольку стандарт не предписывает ни того ни другого
(§2.10, §2.11); calledElement, читаемый как QName (§2.13); и распознавание
вендорского диалекта (§2.14) вовсе вне стандарта — стандарт поставляет механизм
extensionElements и не назначает его содержимому никакого значения.
4. Рассмотренные альтернативы¶
| # | Точка решения | Варианты | Выбрано — почему |
|---|---|---|---|
| A | Дом BPMN-конвертера | (a) пакет ядра; (b) отдельный модуль | (a) — N7 про ответственность, а она сохраняется направлением импорта, а не файлом go.mod: конвертер импортирует модель, никогда наоборот. Самописный stdlib-парсер ничего не добавляет к бюджету зависимостей ядра, поэтому аргумент бюджета в пользу (b) не переживает строку D. Против него: (b) стоит модуля, replace, релизного тега и — решающе — невидимости для гейта diff-coverage, который видит только корневой модуль. |
| B | Обвязка шва | (a) единственная внедряемая реализация в конструкторе движка; (b) самостоятельный реестр с регистрацией по ключу | (b) — требование в нескольких подключаемых форматах; единственная внедряемая опция моделирует одну реализацию, а не ключёванный набор, и привязала бы таблицу форматов к движку, о котором конвертер знать не должен. |
| C | Форма интерфейса | (a) единый Converter{Import;Export}; (b) раздельные Importer/Exporter |
(b) — формат может поддерживать лишь одно направление, а единый интерфейс вынуждает половинчатые реализации заглушать вторую половину. Разделение также зеркалит асимметрию io.Reader/io.Writer, на которой движок уже говорит. |
| D | Реализация парсера | (a) обернуть стороннюю Go-библиотеку BPMN; (b) самописный encoding/xml |
(b) — stdlib покрывает отображение с нулём зависимостей, тогда как существующие библиотеки тяжелы диаграммами и тянут вес, который модулю не нужен. Пересматриваемо на формат; шву всё равно. |
| E | Импортированные id | (a) автогенерация; (b) сохранение BPMN-id |
(b) — ADR-019 ключует версии по id процесса, поэтому авто-id сделали бы каждый импорт одиночной первой версией (§2.5). |
| F | Точность round-trip | (a) побайтово/с сохранением DI; (b) только семантически | (b) — обмен диаграммами вне области исполнения, а текстовая беспотерьность не является требованием соответствия; её сохранение — фича уровня моделировщика со своим носителем в модели (§2.8, §7). |
| G | Доставка «в комплекте» | (a) blank import в стиле image; (b) настоящее умолчание ядра |
(a) — (b) кладёт формат в ядро и противоречит SAD-001 N7, значит требует ревизии SAD; blank import стоит одной строки и сохраняет ядро чистым (§2.3). |
| H | Забор импорта | (a) продолжать нарезать семейства элементов; (b) весь набор execution conformance | (b) — определение является графом, поэтому частичный импортёр импортирует ничего из любого файла, содержащего тот единственный вид, которого ему не хватает. Нарезка не даёт пригодного инкремента до самого последнего среза. Цена — одно крупное приземление; альтернатива — несколько приземлений, каждое из которых поставляет ноль работающих импортов (§2.9). |
| I | Поддержка JUEL | (a) JUEL-движок, зарегистрированный под собственной языковой заявкой; (b) трансляция из исходника в исходник; (c) не поддерживать | (b) — грамматики пересекаются почти полностью, поэтому (a) покупает вечную вторую реализацию каждой семантики выражений ради того, чего (b) и так не лишена. (c) отвергает корпус, поскольку JUEL — это то, что Camunda-файлы реально содержат. Нетранслируемые конструкции отвергаются по имени, но никогда не переписываются частично (§2.10). |
| J | Язык выражения, когда объявление и тело расходятся | (a) уважить объявление, включая схемное умолчание XPath; (b) дать решать разделителям ${…}, что бы ни было объявлено |
(b) — (a) есть буквальное прочтение и отвергает почти каждый реальный документ: моделировщики либо опускают expressionLanguage и пишут ${…}, либо их инструмент явно выписывает XPath-умолчание и затем выдаёт под ним JUEL. Обе формы — один и тот же файл, и отвергнуть любую из них на её собственной неверной маркировке не даёт ничего, чего не даёт корректный импорт. Объявление по-прежнему решает каждое тело, которого разделители не касаются. Зафиксировано как намеренное расхождение, а не сделано втихую (§2.10, §3). |
| K | Немаршрутизируемый формат скрипта | (a) отказать; (b) свалиться на поставляемый движок | (a) — (b) прогоняет синтаксис другого языка через неверный интерпретатор и сообщает ошибку разбора изнутри движка во время исполнения, обвиняя скрипт, а не отсутствующий формат (§2.11). |
| L | DMN | (a) разбирать DMN XML в конвертере; (b) нести непрозрачную ссылку на решение | (b) — (a) противоречит не-цели «никогда не встраивать DMN-движок» и кладёт парсер второго стандарта внутрь BPMN-конвертера. Шов движка правил разрешает ссылки; работа конвертера заканчивается на том, чтобы её донести (§2.12). |
| M | Global task'и | (a) инлайнить копию задачи на каждом месте вызова; (b) отвергнуть семейство; (c) импортировать каждый как процесс, зарегистрированный под своим id | (c) — (a) превращает переиспользование-по-ссылке в дублирование: один global task, вызываемый из трёх мест, импортируется как три несвязанные задачи, а правки оригинала перестают распространяться. (b) честно только пока ничто не может обслужить callable, а нечто может: global task является callable-процессом (ADR-023 v.5 §2.7), поэтому ссылка остаётся ссылкой — одна регистрация, любое число вызывающих, версионируется как процесс, — а роль конвертера в том, чтобы произвести процесс (§2.13). |
| N | Вендорские расширения | (a) молча пропускать; (b) распознать Camunda 7 и отобразить то, у чего есть дом; (c) обобщённый passthrough в носитель модели | (b) — (a) теряет исполняемое содержимое каждого мигрированного файла и вовсе не может доставить ссылку на решение из §2.12. (c) требует нового типа модели, чтобы держать произвольный чужой XML, и заново поднимает вопрос «а что это значит при исполнении?», на который (b) отвечает, отображая только то, что модель уже понимает (§2.14). |
| O | Многопроцессные документы | (a) отвергать collaboration; (b) импортировать первый процесс и отбросить остальные; (c) опциональная способность уровня документа | (c) — (a) противоречит явным словам §2.3.2; (b) отбрасывает определения молча — ровно тот отказ, который требование обратной связи существует предотвращать. (c) аддитивна, оставляет Import и каждого существующего вызывающего нетронутыми и не требует типа модели Collaboration (§2.15). |
| P | Отчёт об отброшенных конструкциях | (a) второе возвращаемое значение у Import; (b) логгер на шве; (c) сложить в Result уровня документа |
(c) — (a) ломает каждого существующего вызывающего; (b) делает отчёт побочным эффектом, который потребитель библиотеки не может инспектировать, а только наблюдать. Одна способность, покрывающая и многопроцессность, и диагностику, держит шов на двух интерфейсах плюс одном опциональном третьем (§2.15). |
5. Последствия¶
Положительные
- Ядро остаётся чистым по зависимостям; XML-поверхность карантинирована в одном пакете, а независимость шва от формата обеспечивается механически.
- Шов является настоящей точкой расширения: XPDL, JSON-DSL или вендорский диалект — это сторонняя регистрация, без изменений в ядре.
convertработает без движка — офлайн-валидация, инструментарий, тесты.- Импортированные определения версионируются правильно (§2.5), поэтому импорт композируется с линией call activity и реестра (ADR-019, ADR-023).
- Ошибки о неподдерживаемых элементах дают моделировщику петлю обратной связи SAD-001 §5, а §2.16 делает читаемым вид отказа, а не только его наличие.
Отрицательные / издержки
- BPMN не является буквальным умолчанием ядра — host добавляет blank import, чтобы его получить.
- Нет round-trip обмена диаграммами: разметка файла теряется на импорте→экспорте. Приемлемо для движка исполнения и проговорено для всякого, кто ожидает round-trip уровня моделировщика.
- Импорт и экспорт — не один и тот же забор, поэтому гарантия round-trip §2.8 держится только над подмножеством экспорта: файл может чисто импортироваться и затем не экспортироваться. Это цена §2.9 — импорт есть то, к чему обязывает §2.3.2 и что нужно внедрению, — и §7 планирует её закрытие.
- Конвертер владеет транслятором. Переписывание JUEL (§2.10) — это парсер и генератор кода, живущие внутри пути импорта, со своей поверхностью корректности и своим режимом отказа: неверное переписывание уводит токены не туда, молча. Правило «отказывать по имени» его ограничивает, но цена реальна.
- Распознавание диалекта — постоянное обязательство. Camunda 7 продолжает меняться, и каждая добавляемая ею конструкция становится вопросом отображения, на который конвертер обязан ответить или отчитаться. Правило 1 §2.14 ограничивает это работой отображения, никогда работой проектирования.
- Отказы — документированный интерфейс. Отвергнутый язык выражений, формат скрипта, ссылка на решение или ссылка на callable — это исход, на котором host ветвится, поэтому их идентичность является API-поверхностью, которую нельзя беспечно перетасовывать.
6. Рекомендации по enterprise-готовности¶
- Фикстуры соответствия. Подключить набор импортных тестов OMG MIWG как приёмочный корпус конвертера.
- Стриминг.
Import(io.Reader)/Export(io.Writer)уже потоковой формы; держать BPMN-реализацию потоковой, чтобы большие определения не вынуждали буферизацию целого файла. - XSD / валидация по схеме. Опциональный строгий режим, валидирующий против XSD OMG до отображения, за опцией.
- Passthrough элементов расширений. Сохранение неизвестных in-scope элементов расширений ради беспотерьного round-trip кастомных пространств имён требует сначала носителя в модели.
- Таргетирование диалекта на экспорте. По умолчанию выдавать чистый OMG BPMN; будущая опция сможет целиться в вендорские пространства имён.
7. Область охвата и отложенное¶
В охвате: шов конвертеров и его реестр (§2.1–§2.4); импортированная идентичность как ключ версионирования (§2.5); импорт по всему набору элементов execution conformance с явной диспозицией для всего вне его (§2.7, §2.9–§2.15); экспорт по подмножеству §2.6 с семантическим round-trip (§2.8); и классификация, которую несёт каждый отказ вне покрытия (§2.16).
Отложено — указатели вперёд, здесь не решается:
- Паритет экспорта с забором импорта. Ближайший преемник и единственное, что закрывает асимметрию §5 и возвращает гарантию §2.8 всему забору. Цена ограничена, только если она уплачена.
- Сохранение обмена диаграммами — round-trip, который моделировщик узнал бы. Требует носителя разметки в модели, а это фича инструмента моделирования, а не исполнения.
- Строгий режим XSD (§6) и второй формат обмена за тем же швом: оба аддитивны, и ни один не меняет здешних решений.
- Привязки сообщений сервисной задачи (
inMessageRef/outMessageRef) — разбираются и записываются, но не привязываются к каталогу сообщений и не выдаются обратно. - BPMN как настоящее умолчание ядра вместо blank import (§2.3, строка G) — ревизия SAD-001 N7 и потому решение SAD.
8. Ссылки¶
Проектные (вверх / вбок, с версиями):
- SAD-001 v.1.3 §4 N5/N7, §5, §9/§9.1/§9.2, §14 — пробел парсера, обратная связь моделировщику, раскладка модулей, область соответствия.
- ADR-002 v.2 — интерфейсы плюс обвязка на этапе компиляции; идиома расширений, которой шов следует и от которой один раз отклоняется (§2.2).
- ADR-019 v.1 — ключ версии = id процесса; ограничение сохранения идентичности (§2.5).
- ADR-003 v.2 — границы модулей и правила направления импорта; конвертер остаётся внутри корневого модуля (строка A).
- ADR-032 v.1 — выражения маршрутизируются по языковой заявке; цель трансляции и отказы §2.10 сформулированы в этом словаре.
- ADR-031 v.1 — скрипты
маршрутизируются по
scriptFormatчерез реестр движков; §2.11 — утверждение о том, что может нести документ, а не о шве. - ADR-027 v.1 — шов движка правил и непрозрачная ссылка на решение, которую несёт §2.12.
- ADR-020 v.4 — модель человеческого взаимодействия, на чей словарь assignee / candidate-user / candidate-group §2.14 отображает диалект.
- ADR-023 v.5 §2.7 — ссылка на callable, резолверный шов и решение «global task как процесс», которым служит §2.13.
- ADR-030 v.1 — элементы данных, в которые
приземляются диспозиции импорта и строка
<import>. - ADR-028 v.2 §2.7 — характеристики транзакции, которые несёт модель, цитируемые правилом «никогда не компенсировать» из §2.16.
- ADR-035 v.1 §2.2 — поставляемый host'ом Router, который делает ad-hoc постоянной границей (§2.16).
- ADR-039 v.1 — ярус артефактов, в который §2.9 сохраняет, и образец «способность приземляется первой» (§2.16).
- ADR-040 v.2 — I/O-контракт уровня процесса,
которым становится
ioSpecificationglobal task'а (§2.13).
SAD-001 сам находится в статусе Draft, поэтому pin'ы выше отслеживают движущуюся базу, пока он не ратифицирован.
Стандарт (BPMN 2.0 KB):
- docs/bpmn-spec/conformance.md — §2.3 Process Execution Conformance (§2.3.1 семантика / §2.3.2 импорт); in-scope-список элементов; DI/DC вне области.
- docs/bpmn-spec/elements/ — структурная метамодель.
- docs/bpmn-spec/semantics/ — поток токенов, задачи, шлюзы, события, конечные события.
Открытые вопросы¶
Нет.
История документа¶
| Версия | Дата | Изменение |
|---|---|---|
| v.1 | 2026-07-17 | Первоначальный драфт. Шов конвертеров (Importer/Exporter плюс реестр с регистрацией по формату в ядре), BPMN как конвертер «в комплекте», MVP-подмножество элементов, общее для обоих направлений, и семантический — не побайтовый — round-trip. |
| v.2 | 2026-07-30 | Принято на первом приземлении. Конвертер является пакетом, а не модулем верхнего уровня (строка A развёрнута): stdlib-парсер не стоит ядру никаких зависимостей, а модуль был бы невидим для гейта diff-coverage. serviceTask присоединяется к подмножеству, а documentation/extensionElements пропускаются тихо, а не отвергаются. |
| v.3 | 2026-08-02 | Принято. Только исправление: забор был определён через ссылку на Common Executable Subclass, который является подклассом Modeling и никогда не применялся. Он перебазирован на элементы, которые анимирует Clause 13, а §2.3.2 признана делающей конвертер вторым из двух требований Process Execution Conformance. Сам набор элементов не изменился. |
| v.4 | 2026-08-10 | Забор импорта сдвигается. Импорт берёт весь набор элементов execution conformance, с явной диспозицией для каждого семейства вне его (§2.9), потому что частичный импортёр не импортирует ничего из любого файла, содержащего тот единственный вид, которого ему не хватает (строка H). Добавлены три языковые политики — JUEL транслируется из исходника в исходник, а нетранслируемые конструкции отвергаются по имени, схемное умолчание XPath намеренно не уважается (§2.10); только самодостаточный исходник скрипта (§2.11); непрозрачная ссылка на решение, никогда не DMN-парсер (§2.12) — распознаваемый диалект Camunda 7 (§2.14) и опциональная способность DocumentImporter, несущая набор процессов документа плюс отчёт об отброшенных конструкциях (§2.15). Экспорт остаётся на подмножестве §2.6, сужая до него гарантию §2.8. |
| v.5 | 2026-08-17 | <import> отображается, а не пропускается: собирается по пространству имён и привязывается к тому <itemDefinition>, чей префикс structureRef в него разрешается, а тот, на который никто не ссылается, попадает в отчёт. Исправление строки диспозиции; контракт не менялся. |
| v.6 | 2026-08-26 | Три артефакта стандарта разбираются и сохраняются в model-only ярус артефактов, а category/categoryValue потребляются как вход разрешения (ADR-039 v.1), на том же прочтении двух обязательств, что и дорожки. §2.16 поглощает правило границ, которое нёс упразднённый ADR-038: три класса, правила «никогда не компенсировать» и «способность приземляется первой» и то, что каждый отказ должен своему читателю. |
| v.7 | 2026-08-28 | Global task'и импортируются как callable-процессы, квалифицированный calledElement несёт своё пространство имён, а §2.10 фиксирует перебивание разделителями. Каждый global task становится процессом в наборе документа — его id, его ioSpecification как контракт ADR-040 v.2, его задача построена тем же прочтением, что и внутрипроцессная форма, — потому что ADR-023 v.5 §2.7 решает, что global task является callable-процессом, который обслуживает реестр; строка M фиксирует разворот отказа из v.4. calledElement читается как QName: префикс собственного пространства имён схлопывается до ключа, импортированное пространство имён едет с call activity для резолвера движка, необъявленное отвергается. §2.10 исправлена: разделители ${…} решают язык, что бы ни объявлял документ, — что конвертер и делает с момента приземления JUEL-транслятора, тогда как этот ADR описывал обратный порядок; объявление теперь решает только те тела, которых разделители не касаются (строка J переформулирована). Документ также переписан на актуальность — blockquote'ы версий, заметки о вытесненных заборах, разделители «добавлено в v.N» и реестр разрешённых вопросов удалены, а pin'ы стандарта перепроверены. |