ADR-028 — Transaction Sub-Process: ACID-подобный abort через Cancel¶
| Поле | Значение |
|---|---|
| Статус | Принято |
| Версия | v.2 |
| Дата | 2026-08-26 |
| Владелец | Руслан Габитов |
| Уточняет | ADR-023 v.3 (модель области выполнения Sub-Process, которую этот ADR варьирует; §2.8 держит Transaction «спроектировано под» в рамках #91 — данный ADR её реализует), ADR-026 v.1 §2.2/§2.4 (журнал завершений + обратно-упорядоченный проход по всей области, который потребляет cancel), ADR-018 v.1 §2.7 (область триггеров границ, которая отложила Cancel — этот ADR снимает отсрочку для Transaction), ADR-006 v.4 (определение события Cancel + класс прямого разрешения), ADR-013 v.2 (факт KindScope/PhaseCanceled, о котором abort сообщает; множество scope-фаз открыто), ADR-001 v.6 §4 (loop единственного писателя, владеющий всем состоянием cancel/compensation) |
EN-оригинал — канонический: ADR-028-transaction-sub-process.md. Этот файл — его перевод (twin).
Transaction Sub-Process — последний живой кусок эпика продвинутого Sub-Process (#91): Sub-Process с ACID-подобной семантикой, чья отличительная черта — координированный abort: при отмене он откатывает свою уже завершённую работу и сносит то, что ещё выполняется, а затем передаёт управление наружу через выделенную границу. Всё, что для этого нужно, уже существует: журнал завершений и обратно-упорядоченный компенсационный проход (ADR-026), примитив cancel по всей области (ADR-023 §2.5) и определение события Cancel (ADR-006). Этот ADR решает, как они компонуются в transaction abort.
1. Контекст и проблема¶
Обычный Sub-Process (ADR-023) выполняет свой внутренний граф во вложенной области выполнения и завершается, когда область осушается. BPMN добавляет вариант Transaction (sub-processes.md §Transaction, §10.7): выполнение идентично обычному Sub-Process за исключением одного нового поведения — достижение Cancel End Event внутри Transaction запускает transaction abort, а Cancel intermediate boundary event, прикреплённое к Transaction, — это маршрут, которым управление уходит при этом abort. Спецификация определяет abort как: завершить все выполняющиеся Activity и компенсировать все успешно завершённые Activity (§10.7), после чего Transaction покидается аномально через свою Cancel boundary.
Все части были налицо, но не связаны, когда писалась v.1 (приземление v.1 их связало; этот раздел — та исходная точка):
CancelEventDefinitionсуществует и является валидным триггером End Event; Cancel boundary был отложен (ADR-018 §2.7 ограничил границы триггерами 0.1.0, оставив Cancel/Multiple на потом).- Журнал завершений + обратно-упорядоченный проход по всей области (ADR-026 §2.2/§2.4) уже компенсируют все завершённые activity области — ровно ту половину «компенсировать все успешно завершённые».
- cancel по всей области (ADR-023 §2.5) уже завершает каждый выполняющийся трек под путём области — половину «завершить все выполняющиеся».
- У движка не было типа Transaction, понятия, что Cancel легален только внутри него, и обработчика, секвенирующего abort.
Проблема, которую решает этот ADR: определить вариант Transaction и abort, который он выполняет, скомпоновав приземлённые примитивы журнала/прохода/cancel — без нового механизма отката.
2. Решение¶
2.1 Transaction — это вариант Sub-Process, а не новый узел¶
Transaction Sub-Process является встроенным Sub-Process (ADR-023 §2.2),
несущим характеристики транзакции: объект-значение, скомпонованный в единый
тип Sub-Process — та форма, которую ADR-025 §2.1 даёт итерации, — а не отдельный
вид узла и не обёртка вокруг него. Характеристики держат то, что специфично для
транзакции, — method abort'а и protocol координации (§2.7); единица,
исполняющая узел, читает их при открытии области, как runtime итерации читает
loop characteristics, и делает перед запуском узла один дополнительный шаг: она
связывает (bind) выполнение с ними — разрешает method в координатор, который
будет abort'ить эту область, и передаёт этому координатору protocol, — после чего
узел выполняется как обычный Sub-Process, у abort'а которого есть владелец. Пока
compensate — единственный координатор, связывание разрешается в собственную
последовательность движка §2.3 и ничего наблюдаемого не меняет; это точка, в
которой позже подключится зарегистрированный хостом координатор (§2.8), без
изменения исполняющей единицы.
Связывание достигает и внутренних узлов. Узел, исполняемый внутри связанной области, исполняется под её связыванием: его собственная исполняющая единица сообщает координатору области о старте, завершении и отказе узла, так что координатор узнаёт, какая работа принадлежит транзакции, от самих узлов — а не из транзакционного handle'а, протянутого через швы операций, хранилищ данных и worker'ов. Для compensate это уже происходит: журнал завершений, который ADR-026 ведёт по области, — это и есть сообщение координатору «этот узел завершился», а проход §2.3 — координатор, действующий по нему. «Является ли транзакцией» — производный вопрос присутствуют ли характеристики; Sub-Process, который их не несёт, ни с чем не связан и не затронут ничем ниже.
Его нормальное выполнение — открытие области, seed, drain, граничные события, данные, вложенные композиты — это семантика обычного Sub-Process, без изменений (sub-processes.md: «семантика выполнения самого Transaction Sub-Process следует семантике обычного Sub-Process; поведение Cancel — это отличительная черта»). Сегодня характеристики меняют ровно две вещи: они разрешают Cancel (End + boundary), который обычная activity запрещает (§2.6), и называют область, которую abort'ит cancel (§2.3) — ту, которой шаг связывания даёт владельца.
Именно эта область — причина, по которой характеристики скомпонованы в Sub-Process, а не обёрнуты вокруг него: abort проходит по её журналу и отменяет её треки, Cancel End спрашивает, находится ли он внутри той области, а Cancel boundary прикрепляется к тому хосту (§2.4). Узел-обёртка расщепил бы узел в графе и область, которая abort'ится, и каждому из этих правил пришлось бы его разворачивать.
Transaction достигает одного из трёх терминальных исходов:
- Success — внутренний граф осушается нормально; Transaction завершается, и токен уходит по своему нормальному исходящему потоку (поведение обычного Sub-Process).
- Cancellation — внутри достигается Cancel End Event; Transaction abort'ится (§2.3), и управление уходит через Cancel boundary (§2.4).
- Hazard — вырывается ошибка, которая ни перехвачена, ни является чистым cancel; это существующий путь ошибки (ADR-006 §2.6, распространение по цепочке областей), без изменений. Этот ADR не добавляет обработку hazard.
2.2 Cancel — это событие прямого разрешения, обрабатываемое на loop¶
Cancel — это не broadcast. Стандарт классифицирует Cancellation как событие прямого разрешения (event-handling.md: «Триггер, направленный на конкретный экземпляр Process или Activity») и разрешает его только на Transaction Sub-Process (всегда прерывающий). Поэтому Cancel End Event не уходит через EventHub так, как это делает throw Signal или Message — получатель ровно один (охватывающий Transaction), известный статически.
Поэтому движок обрабатывает Cancel End Event на loop единственного писателя —
в том же locus, который уже использует scoped Terminate (ADR-023 §2.5 —
terminateScope разрешает Terminate End Event относительно охватывающей области,
не касаясь хаба). loop распознаёт триггер Cancel, разрешает ближайшую
охватывающую область Transaction, выполняет abort против неё и возобновляет
хост Transaction на его Cancel boundary. Никаких подписок, никакой корреляции,
никакой межгорутинной доставки — локальное, детерминированное разрешение.
(Cancel End Event, не охваченный Transaction, — это ошибка моделирования, отвергаемая на валидации (§2.6), так что разрешение на loop никогда не остаётся без цели.)
2.3 Последовательность abort — компенсировать завершённые, затем завершить выполняющиеся¶
При Cancel область Transaction abort'ится в фиксированном порядке, и порядок несущий:
- Компенсировать завершённые activity — выполнить обратно-упорядоченный проход по всей области из ADR-026 над журналом завершений области Transaction. Каждая успешно-завершённая внутренняя activity, у которой есть компенсационный обработчик, выполняет его против своего захваченного снимка данных (ADR-026 §2.5), от самой недавно завершённой к более ранней.
- Завершить всё ещё выполняющиеся activity — снести каждый трек, всё ещё живой под областью Transaction (cancel по всей области из ADR-023 §2.5).
- Уйти через Cancel boundary — хост Transaction возобновляется на исходящем потоке Cancel boundary (§2.4); область Transaction закрывается.
Почему компенсация-до-завершения (правило выживания журнала). Примитив cancel по всей области отбрасывает журнал завершений — отменённая работа не компенсируема, так что обычный cancel её сбрасывает. Но transaction abort должен компенсировать ровно этот журнал. Поэтому abort обязан сначала пройти по журналу, пока тот цел, и только затем отменить остаточные выполняющиеся треки. Выполняющиеся activity никогда не завершались, поэтому их нет в журнале и они не компенсируются — они просто завершаются. Секвенирование компенсации впереди сноса — это всё содержание корректности abort; в остальном оба примитива переиспользуются дословно.
Компенсационный проход синхронен относительно abort: Transaction не уходит через свою Cancel boundary, пока не отработал каждый компенсационный обработчик (барьер завершения прохода, ADR-026 §2.4). Это сохраняет «компенсировать все завершённые до того, как управление уйдёт» — ACID-подобную гарантию.
2.4 Cancel boundary — смоделированный выход (снимает отсрочку ADR-018 §2.7)¶
Управление покидает abort'ящийся Transaction через прикреплённое к нему Cancel
intermediate boundary event
(sub-processes.md: «Cancel
intermediate boundary event МОЖЕТ быть прикреплено к Transaction Sub-Process —
управление уходит через него при отмене»). Этот ADR снимает отсрочку с
триггера Cancel boundary, который ADR-018 §2.7 оставил на потом — узко: Cancel
boundary разрешено только на Transaction Sub-Process, всегда прерывающее
(у Cancel нет непрерывающей формы,
event-handling.md: «всегда
true») и является единственным легальным домом для Cancel boundary.
В отличие от обычного граничного события, Cancel boundary не взводится как hub-waiter — согласованно с §2.2, Cancel никогда не достигает хаба. Это выход, объявленный в модели: loop, отработав abort (§2.3), маршрутизирует хост на исходящий поток границы напрямую. Роль границы — диаграммная: она делает выход abort видимым и маршрутизируемым — тогда как loop ведёт разрешение. Transaction без Cancel boundary, который abort'ится, просто завершается аномально-и-локально без исходящего токена (родитель продолжает мимо него) — та же форма, которую сегодня принимает scoped Terminate без границы.
2.5 Чего cancel не делает¶
- Он не распространяется как ошибка вверх по цепочке областей — cancel — чистый, смоделированный abort, а не сбой. Родительский процесс не затронут, помимо токена, который уходит (или не уходит) из Cancel boundary.
- Он не компенсирует через границу Transaction — обходятся только собственные завершённые внутренние activity Transaction. Завершённый Transaction, который позже компенсируется снаружи, — это обычный путь ADR-026 (Transaction как завершённая activity может сам нести компенсационный обработчик в своей родительской области), без изменений здесь.
- Он не повторно входит в завершённую Call Activity — межэкземплярная компенсация остаётся вне области (ADR-026 §2.9), Call Activity никогда не попадает в журнал.
2.6 Правила формы (валидация)¶
Обеспечиваются при конструировании модели / регистрации, fail-fast:
- Cancel End Event легален только внутри графа Transaction Sub-Process (напрямую или транзитивно в пределах его области). В прочих местах → отвергается.
- Cancel boundary event прикрепляется только к Transaction Sub-Process и всегда прерывающее. На любой другой activity → отвергается.
- Transaction Sub-Process может нести не более одного конвенционального Cancel boundary; вложенные Transaction вне области (§2.8).
- Маркер Transaction взаимоисключающ с маркером Event Sub-Process (обработчик сам по себе не транзакция).
Это зеркалит правила формы Event Sub-Process (ADR-023 §2.10) — маркер, который разрешает конструкцию, иначе запрещённую, проверяемый однократно на этапе сборки.
2.7 method выбирает координатор; protocol передаётся ему и никогда не читается¶
У Transaction в BPMN два собственных атрибута сверх Sub-Process, который он
расширяет, — method и protocol
(activities.md §Transaction: оба
String, 0..1). Схема типизирует method как tTransactionMethod —
объединение трёх токенов ##Compensate, ##Store, ##Image и любого URI,
по умолчанию ##Compensate (OMG Semantic.xsd, tTransaction); метамодель
пишет те же три как compensate, store, image. protocol — атрибут
метамодели, которого схема не объявляет вовсе (tTransaction расширяет
tSubProcess одним method), так что строго-схемный инструмент его никогда не
пишет, а моделлер, ведомый метамоделью, может.
method — открытый идентификатор, называющий координатор, который abort'ит.
Член anyURI в схеме делает множество значений открытым по замыслу, и движок
держит его открытым: method — типизированный идентификатор, а не перечисление.
Единственный координатор, который движок предоставляет сам, — compensate
(откат запуском компенсационных обработчиков завершённых activity,
последовательность §2.3), и он же по умолчанию: Transaction, не называющий метод,
— это compensate-транзакция, и токен схемы ##Compensate, и написание метамодели
compensate обозначают его. store, image или URI называют координацию,
которую движок не выполняет, — координацию менеджеров ресурсов уровня менеджера
транзакций (стиль WS-AT / XA) для первых двух, метод, определённый протоколом, для
третьего. Модель их не отвергает — она несёт любой идентификатор; они
отвергаются на регистрации, где процесс сверяется с движком, в котором будет
выполняться, с причиной для этого метода не зарегистрирован координатор. Пока
шва координации нет (§2.8), это каждый метод, кроме compensate, и отказ говорит
смоделировать откат компенсационными обработчиками; когда хост сможет
регистрировать координаторы, та же проверка пропустит всё, что он
зарегистрировал.
Именно модель читает формы атрибута и держит значение, а конвертер отображает атрибут на неё дословно: правило ADR-024 §2.16 в применении здесь — формат документа не держит второй копии множества значений, которой было бы с чем разойтись.
protocol — непрозрачная строка, передаваемая координатору. Вендоренная
выжимка не определяет его дальше, и ничто в движке его не читает: пока compensate —
единственный встроенный координатор, потребителя сегодня нет, а
зарегистрированный координатор — единственное, что им когда-либо станет. Модель
его несёт рядом с method — чтобы документ, который его называет, загружался
целиком, а round-trip его переизлучал: то же model-only-обязательство, которому
отвечают lanes и артефакты (модель держит то, что заявляет диаграмма), — а
выполнение передаёт его дальше нетронутым. protocol можно задать только вместе с
характеристиками транзакции; на обычном или Event Sub-Process это ошибка
конструирования, потому что метамодель помещает атрибут только на Transaction.
2.8 Спроектировано под & вне области¶
- Шов координации транзакций — что такое координатор: координатор,
регистрируемый хостом на каждый
method(реестр уровня thresher рядом со швами скриптов, правил и worker'ов из ADR-002), сессия, которую шаг связывания (§2.1) открывает на нём — фиксируемая при drain области, abort'ируемая внутри §2.3, — и то, что он делает с сообщениями узлов, которые доставляют внутренние связывания (§2.1): работа, которую он должен откатить, приходит как собственные сообщения узлов о старте/завершении/отказе — обобщённый журнал. Спроектировано под: §2.1 решает, где он подключается (шаг связывания — на узле Transaction и на каждом узле внутри него), §2.7 — что его выбирает (открытый идентификатор, несомыйprotocol); сам контракт координатора — что такое сессия, что несёт сообщение узла, как не-compensate abort сочетается с §2.3 — решается в собственной концепции, не здесь. - Вложенные Transaction — Transaction внутри Transaction; вне области сейчас (разрешение abort нуждается в ближайшем охватывающем Transaction, что вполне определено, но упорядочивание отмены вложенных транзакций и взаимодействие границ заслуживают собственной концепции). Отвергаются на валидации.
- Авто-компенсация по умолчанию при ошибке (presumed-abort) — Transaction, претерпевший неперехваченную ошибку, авто-компенсирующий до того, как ошибка распространится (вторая половина §10.7), — это тот же проход по журналу, запущенный с пути ошибки; он здесь спроектирован под, но решается вместе с соавторингом пути ошибки ADR-026 §2.9, а не приземляется в этом ADR. До тех пор поведение Transaction при неперехваченной ошибке — текущее распространение ошибки conformant-подмножества (ADR-006 §2.6), отмеченное как engine note (§2.9).
- Durable-восстановление транзакции (регидратация Transaction в полёте после краха) — едет по линии персистентности ADR-009; вне области.
2.9 Engine notes (отклонения & выборы)¶
- Transaction — композиция, а не подкласс (§2.1) — стандарт выводит
TransactionизSubProcess; движок компонует характеристики в единый тип Sub-Process, как делает это для итерации (ADR-025). Та же наблюдаемая форма, один тип узла. - Нет координации
store/image(§2.7) — намеренно, пока нет шва (§2.8); используйте компенсационные обработчики. protocolинертен (§2.7) — несётся ради загрузки, round-trip и будущего координатора, никогда не читается выполнением и отвергается без характеристик транзакции.- Авто-компенсация при неперехваченной ошибке не автоматична (§2.8) — неперехваченная ошибка в Transaction сегодня распространяется по ADR-006 §2.6; авто-проход presumed-abort — это follow-up. Модель, желающая отката-при-ошибке, прикрепляет явную error boundary, которая throw'ит Compensation.
- Cancel локален для loop, никогда не виден хабу (§2.2) — механизм движка, примиряющий событие прямого разрешения с loop единственного писателя; невидим моделирующему.
3. Обоснование стандартом¶
- Вариант Transaction & Cancel — sub-processes.md §Transaction; end-events.md §Cancel End Event («Аномальное завершение Sub-Process + transaction abort; управление уходит через Cancel boundary event»; «Не валиден на уровне Process»).
- Abort = завершить выполняющиеся + компенсировать завершённые — §10.7 (правила Cancel / Transaction), выведенные в event-handling.md §Cancel («Разрешён только в Transaction Sub-Process. Отменяет Sub-Process и abort'ит связанную Transaction»).
- Cancel — прямое разрешение, только Transaction, всегда прерывающий —
event-handling.md (Cancel: «да —
только на Transaction Sub-Process»; cancelActivity «всегда
true»). - Атрибуты
methodиprotocol— activities.md §Transaction (protocolString 0..1,methodString 0..1; вендоренная метамодельbpmn-spec/scripts/bpmn-moddle.json,Transaction, несёт оба). Схема — OMGSemantic.xsd,tTransaction— объявляет одинmethod, типизированный какtTransactionMethod(объединение токенов##Compensate/##Image/##Storeиxsd:anyURI, по умолчанию##Compensate), и никакогоprotocol; чтение двух написаний в §2.7 и перенос бессхемного атрибута оба следуют из этого расщепления. - Там, где gobpm сужает стандарт (методы
store/image/URI, вложенные, авто-проход-при-ошибке, неинтерпретируемыйprotocol), это отмечено как явный engine note (§2.9), никогда не так, будто этого требует стандарт.
4. Рассмотренные альтернативы¶
- Cancel через EventHub (broadcast + boundary-catch), переиспользуя обычную
машинерию границ. Отвергнуто: Cancel — событие прямого разрешения с
единственным, статически известным получателем; его broadcast — семантическое
несоответствие, а прерывающий снос пути boundary-catch (
cancelHostScope) отбрасывает журнал, который abort должен компенсировать (§2.3). Разрешение, локальное для loop, и вписывается в класс стандарта, и сохраняет журнал. - Зачисление через хостовые швы (транзакционный handle в контексте, который получает каждая операция, хранилище данных и worker). Отвергнуто: это делает каждый шов осведомлённым о транзакциях, а каждую хостовую реализацию — ответственной за отчётность; связывание исполняющих единиц внутренних узлов (§2.1) даёт координатору то же знание изнутри движка, один раз, и это то, что журнал уже делает для compensate.
- Связывание на регистрации, а не при выполнении. Отвергнуто: регистрация может лишь проверить, что координатор для метода существует (§2.7), и делает это; связывание — на экземпляр области: сессия, которую открывает координатор, принадлежит одной выполняющейся Transaction, — так что это шаг исполняющей единицы, делаемый при открытии области, как и для итерации.
- Булев маркер (
isTransactionиз v.1). Заменён в v.2: как только у Transaction появляются собственные атрибуты и, позже, координатор, флаг рассыпает их по полям Sub-Process; объект характеристик владеет ими, валидация читает одно значение, а исполняющая единица утверждает один тип — форма ADR-025. - Одночленное перечисление
method(только compensate, закрытое). Отвергнуто: членanyURIв схеме делает множество открытым, и закрытие потребовало бы изменения модели в день регистрации координатора; открытый идентификатор, проверяемый на регистрации, не стоит ничего ни сейчас, ни потом. - Узел-декоратор, оборачивающий Sub-Process. Отвергнуто: область транзакции есть область Sub-Process (§2.1), и обёртка расщепляет узел и область, о которой рассуждает каждое правило abort'а и формы.
- Отдельный тип узла
Transaction(а не маркер Sub-Process). Отвергнуто: стандарт явно указывает, что выполнение Transaction есть выполнение Sub-Process; отдельный тип продублировал бы всю модель области/drain/границ/данных ради одного добавленного поведения. Маркер (как у Event Sub-Process) — минимальная, правдивая форма. - Порядок завершить-затем-компенсировать (сначала отменить область, затем компенсировать). Отвергнуто: cancel по всей области отбрасывает журнал завершений, так что компенсации нечего было бы обходить — порядок вынужден правилом выживания журнала (§2.3).
- Отдельный журнал/проход transaction-cancel, отличный от компенсации. Отвергнуто: проход ADR-026 уже и есть «выполнить откат завершённых activity в обратном порядке»; transaction-cancel — это тот же проход с другим триггером, а не другой машинерией (ADR-026 §2.9 предвидел ровно этого потребителя).
5. Последствия¶
- Движок получает Transaction Sub-Process — пятый и последний вариант BPMN композиции activity (обычный, Event-Sub, Call Activity, loop/MI, теперь Transaction) — закрывая эпик #91 и строку conformance по продвинутому Sub-Process.
- Триггер Cancel boundary становится доступен, узко (только Transaction); отсрочка ADR-018 §2.7 закрывается in-place link-аннотацией (не bump'ом), а нота «спроектировано под» из ADR-023 §2.9 аналогично аннотируется как выполненная.
- Никакого нового механизма отката, никакого нового пути доставки событий: abort — это композиция приземлённых примитивов (проход по журналу + cancel области + разрешение, локальное для loop), так что поверхность изменения — маркер, два правила формы, снятый с отсрочки триггер границы и один обработчик секвенирования abort на loop.
- Авто-проход presumed-abort при ошибке (§2.8) остаётся follow-up'ом, «спроектированным под», сохраняя упорядочивание отмены пути ошибки единым будущим соавторингом, а не двумя конкурирующими.
- v.2 меняет форму модели, а не abort: Sub-Process несёт один объект-значение транзакции вместо флага, схемно-валидный документ загружается, ни один конвертер не держит собственной таблицы значений, а шов координации (§2.8) не требует дальнейшего изменения модели. Переизлучение ждёт экспортной половины забора ADR-024 (§7 там): пока Sub-Process вообще нельзя экспортировать, обязательство round-trip выполнено только на стороне модели.
- Сопровождающий SRD приземляет это против текущего субстрата; имена кодовых символов здесь намеренно отсутствуют — это обоснование принадлежит SRD.
История документа¶
| Версия | Дата | Автор | Изменение |
|---|---|---|---|
| v.1 | 2026-07-22 | Руслан Габитов | Первоначальный черновик — Transaction Sub-Process как вариант Sub-Process (isTransaction), чьё поведение Cancel — отличительная черта: Cancel End Event запускает локальный для loop, прямого разрешения abort (Cancel никогда не достигает хаба), секвенированный компенсировать-завершённые (обратно-упорядоченный проход по журналу из ADR-026) → завершить-выполняющиеся (cancel области из ADR-023) → уйти через Cancel boundary, порядок вынужден правилом выживания журнала (cancel области отбрасывает журнал, который abort должен обойти). Снимает отсрочку с триггера Cancel boundary (ADR-018 §2.7) узко — только Transaction, всегда прерывающий, выход, объявленный в модели, а не hub-waiter. method = compensate только (store/image non-goal); вложенные Transaction и авто-компенсация presumed-abort при ошибке спроектированы под, но вне области. Переиспользует приземлённые журнал/проход/cancel дословно; никакого нового механизма отката. Обоснован стандартом против §10.7 / sub-processes / end-events / event-handling. |
| v.1 | 2026-07-24 | Руслан Габитов | Принято — приземлено сопровождающим SRD на этапах M1 (модель), M2 (runtime abort), M3 (e2e + пример): маркер WithTransaction(), локальное для loop разрешение evTransactionCancel, последовательность компенсировать → завершить → Cancel-boundary через флаг scopeEntry.aborting + проход в режиме ожидания, и всегда-прерывающая Cancel boundary как выход, объявленный в модели. Только флип статуса, без изменения концепции (без bump версии). Правки при принятии: Refines закрепляется на ADR-023 §2.8 (bullet «спроектировано под», не §2.9) и observability ADR-013 на факт KindScope/PhaseCanceled, который abort переиспользует (без новой фазы — Option A, без флипа ADR-013). |
| v.2 | 2026-08-26 | Руслан Габитов | Маркер становится характеристиками транзакции (§2.1), и собственные атрибуты Transaction получают в них дом (§2.7). Объект-значение, скомпонованный в единый тип Sub-Process — форма, которую итерация уже имеет по ADR-025, — заменяет голый булев isTransaction; исполняющая единица читает его при открытии области и связывает выполнение с координатором по method, передавая ему protocol, и каждый внутренний узел выполняется под этим связыванием, его исполняющая единица сообщает координатору о старте/завершении/отказе — обобщённый журнал ADR-026; сегодня всегда встроенная последовательность compensate, позже — зарегистрированная хостом; «является ли транзакцией» — производный вопрос. method — открытый идентификатор (tTransactionMethod схемы допускает любой URI): compensate — встроенный координатор по умолчанию, читаемый в обеих стандартных формах — токен схемы ##Compensate и написание метамодели compensate, — а любой другой метод отвергается на регистрации как координатор не зарегистрирован, а не моделью; владение разбором в модели — правило ADR-024 §2.16, и оно списывает собственную таблицу значений конвертера, которая читала только строчное написание и отвергала схемно-валидный файл. protocol — в метамодели, отсутствует в схеме — несётся как непрозрачная строка ради загрузки, round-trip и будущего координатора, никогда не читается выполнением, отвергается без характеристик. §2.8 фиксирует шов координации (реестр, сессия, зачисление) как спроектированный-под и отложенный в собственную концепцию; §2.9 отмечает композицию-не-подкласс; §4 добавляет отвергнутые булев флаг, закрытое перечисление, узел-обёртку, зачисление через швы и связывание на регистрации. Семантика abort'а не изменена. Закрывает транзакционную половину вопроса покрытия, заведённого как #324. |
| v.2 | 2026-08-27 | Руслан Габитов | Принято — приземлено SRD-095 на этапах M1 (объект характеристик и опции), M2 (проверка на регистрации), M3 (связывание области и transaction_method на фактах abort'а), M4 (импортёр отображает оба атрибута дословно), M6 (пример). Попутно FR-8 исправил ранее существовавший дефект чекпоинтов, вскрытый тестом восстановления: документ, снятый на ожидании сразу после компенсируемой activity, терял её запись в журнале. Экспорт Sub-Process — и, значит, переизлучение двух атрибутов — по-прежнему ждёт экспортного среза ADR-024. |