# Роль SMALUM — BPMN
<!-- smalum-role-version: 1.11.0 -->

Узкая шпаргалка нотации для внешней LLM (ENH-1624). Склейка: [role-core.md](https://docs.smalum.io/role-core.md) + этот файл.
Полный каталог: https://docs.smalum.io/role.md · страница: https://docs.smalum.io/ai · эталоны: https://docs.smalum.io/role-examples.md

Файл: https://docs.smalum.io/role-bpmn.md

# Часть A — BPMN → Smalum

Канон языка: https://docs.smalum.io/bpmn · RFC [v0.2](https://docs.smalum.io/rfc/bpmn/v0.2) — §8 коды `BPMN0xxx`, §13 EBNF, §9 антипаттерны «нельзя → вместо».

**Выдача ИИ — только полный язык** (`pool` / `task` / `start` / `xor`). Скетч-сигилы (`!` / `!!` / `?` / `id(`) не пишите. Один файл — один заголовок; `// SM:` только в конце, если просили места.


## A1. Process vs collaboration

| | Process | Collaboration |
|--|---------|----------------|
| Пулы | один | несколько |
| Между сторонами | нет | только **message** `--` |
| Внутри пула | **sequence** `-` | то же |

Choreography / conversation — **не покрываем**.

## A2. Потоки: можно / нельзя

| Можно | Нельзя |
|-------|--------|
| Sequence внутри одного пула: `A - B` | Sequence между пулами (в т.ч. из `-->` / `->`) — **ошибка**, пишите `A -- B` |
| Message между пулами: `A -- B` (без `>`) | `--` между задачами **одного** пула — **ошибка**, без coerce; пишите `-` |
| Association к `data`/`store`: `pay -- ordersDb` или `pay - ordersDb` | Управление через store как sequence «сам ушёл» |
| Ветки шлюза в `{ - цель подпись }` / `~ default` | Ветки xor/or **без** подписи условия |
| Join без `{` + потоки **на** join; сахар `} join joinId` сразу после `{ - a / - b }` | Несколько входящих sequence **в задачу** без join; дубль `шлюз - цель`, если цель уже в `{ }`; сахар `} -> next` **без** имени join |
| Несколько `end` после XOR; сход веток в один `end` без join | Event gateway + второй `event` как join (нужен `xor` join); обрыв на задаче без `end`; **задача без входящего** sequence |
| Условный поток без шлюза: `A -? B условие` | Mermaid `flowchart` / PlantUML activity **вместо** BPMN |

**Join:** XOR с независимыми хвостами или сход в один `end` — join не нужен (конец не «работа»). AND/OR при общем продолжении — симметричный join. AND/OR без join в разные `end` — редко; парсер предупредит.

**Качество:** неверный `-`/`--` по границе пула — **ошибка** (не coerce). Парсер также предупреждает: симметрия AND/OR; условия на ветках xor/or; завершаемость; слияние только через шлюз; неизвестный id цели ветки. Стиль имён (глагол у задачи, вопрос у шлюза) — в выдаче роли, парсер не проверяет язык.

### Жёсткое правило: нет фигур без связей

В BPMN **не бывает блоков без связей, если это не контейнер.** Объявить `start` / `task` / `end` внутри `transaction` или `event subprocess` недостаточно — сразу пишите потоки.

| Фигура | Минимум связей |
|--------|----------------|
| `start` | исходящий sequence (`txIn - book`) |
| `end` | входящий sequence (`book - txOk`) |
| задача / `call` / `catch` / `throw` / шлюз | **входящий и** исходящий sequence (кроме чистого `end`); висячая задача без входа — брак |
| boundary (`+ interrupting …`) | исходящий exception (`bookFail - failed`) |
| `data` / `store` | association (`pay -- ordersDb`) |
| `note` / `примечание` | association к фигуре (`pay -- why` или `pay - why`); парсер рисует пунктир **без** стрелки |
| контейнер (`pool` / `lane` / `subprocess` / `transaction` / `event subprocess` / `group`) | **можно без** sequence на саму рамку: event subprocess **не** вставляют в `go - prepare - bookTx`; внутри рамки дети всё равно связаны |
| свёрнутый `subprocess id` (без `{`) | как задача: входящий и исходящий sequence (`go - check - done`) |

Пример внутри transaction / event subprocess:

```
transaction bookTx {
  start txIn
  task book service
  end txOk
}
event subprocess onFail {
  start error alarm
  task compensate user
  end alarmDone
}
txIn - book - txOk
alarm - compensate - alarmDone
```

Два исхода — `xor`, не два конца без входящих. Парсер может достроить линейную цепочку внутри рамки, если вы забыли `-`; **в выдаче роли всегда пишите потоки явно.**


## A3. Синтаксис `//smalum/bpmn` (канон выдачи)

```
//smalum/[каталог/]bpmn Название

pool client {
  start message requestReceived
  task fillForm user
  end done
}

pool system {
  start msgStart
  task validate auto
  xor result {
    - approve да
    - reject нет
    ~ otherwise иначе
  }
  and split {
    - pack
    - bill
  }
  and joinPack
  task approve service
  task reject
  task otherwise
  task pack
  task bill
  end finished
}

client.requestReceived -- system.msgStart Заявка
system.msgStart - system.validate - result
approve - split
reject - finished
otherwise - finished
pack - joinPack
bill - joinPack
joinPack - finished
system.approve -- client.done Решение
```

**Слова:** `pool`/`пул`, `lane`/`дорожка`, `task`/`задача`, `call`/`вызов`, `start`/`старт`, `end`/`конец`, `catch`/`перехват`, `throw`/`отправка`, `xor`/`and`/`or`/`event`, `subprocess`, `event subprocess`, `transaction`, `group`, `data`, `>>`/`<<` (`вход`/`выход`), `store`, `note`/`примечание`. Условный поток без шлюза: `pay -? done если оплачено`. Непрерывающий старт только в event subprocess: `start non-interrupting timer 1h tick`.

**Типы задач:** `user`, `manual`, `service`, `auto`, `script`, `rule`, `wait`, `receive`, `send` (+ RU-синонимы). Маркеры: `loop`, `parallel`, `sequential`, `adhoc`. После **MI** (`parallel` / `sequential`) можно число экземпляров: `3`, `1..n`, `n` (`task brief user sequential 3`); после `loop` / `adhoc` кардинал **не** пишите.

**Подпроцесс и call**

| Нужно | Пишите | Не пишите |
|-------|--------|-----------|
| Свой процесс, детали не важны | `subprocess check = Проверить` (без `{`) — коробка с **плюсом** | `call check` — это чужой процесс (двойная рамка) |
| Свой процесс, раскрыть шаги | `subprocess check { start … / task … / end … }` — рамка **без** плюса | |
| Ссылка на отдельный процесс | `call shipping = Доставка` | `subprocess` без `{` «вместо call» |
| Ad-hoc порядок шагов в рамке | `subprocess research adhoc { … }` или `group loose adhoc { … }` — тильда на рамке | `adhoc` на `transaction` / `event subprocess` |
| Ad-hoc маркер **задачи** | `task free user adhoc` | путать с `subprocess … adhoc {` |

**Примечание (text annotation):** `note why = Оплата через внешний шлюз` + `pay -- why` (или `-`). Это фигура **на схеме** (Camunda `textAnnotation`), не заметка дока холста.

**События:** `message`, `timer 2h`, `signal`, `error`, `escalation`, `compensate`, `terminate` (end), `link`, `cancel` (end transaction и boundary). Boundary: `task pay service + interrupting timer 30 timeout`. **Модификатор обязателен**, когда тип ясен из рассказа (§0.5): без него на холсте пустой кружок (None), даже если в подписи написано «сигнал» или «сообщение».

**Id:** без дефисов/пробелов; в потоках без слова вида (`go`, не `start go`). Подпись из id или `= Текст`. Цепочки: `a - b - c`. **Перенос в подписи:** два и более пробела подряд = новая строка (`task a = Первая  Вторая`); либо отступ ≥2 на следующих строках.

**Вне диалекта:** complex gateway m-из-n, **условное событие** (conditional event), choreography, conversation, пустой пул без фигур, сахар `} -> next` без имени join, executable XML/движок. В диалекте: явный join и сахар `} join id`; свёрнутый `subprocess`; `adhoc` на рамке `subprocess`/`group`; MI-кардинал после `parallel`/`sequential`; `note`. Условный **поток** без шлюза — это `-?`. `cancel` — end transaction **и** boundary на задаче в transaction.

**Синонимы:** `шлюз` / `gateway` = XOR (не event-gateway). Event subprocess — два слова: `event subprocess id {`.

**Оверлей BPMN:** `// SM: node kiosk.greet …` — путь как в редакторе, не голое `greet` при пуле. `start desire` → `kiosk.desire`, не `kiosk.start`.

**Антипаттерны:** `client.task - system.task` (ошибка); `qualityOk -- clean` внутри одного пула (ошибка); ромб без веток; ветки xor без подписи; две задачи «ОК / неОК» вместо `xor`; несколько входов в задачу без join; задача без входящего sequence; `- outside Подпись` плюс отдельно `task real = Подпись` и `outside - real` (две фигуры — пишите сразу `- real Подпись`); обрыв на задаче; сигилы скетча; UI-клики как отдельные задачи; оверлей новой схемы со стартом правее следующих шагов; **`call` вместо свёрнутого своего `subprocess id`**; комментарий `// …` вместо `note` на схеме. **Стиль имён:** задача — глагол (`= Обработать счёт`); событие — состояние/триггер (`= Заказ получен`); шлюз — вопрос (`= Одобрено?`), ответы на исходящих потоках.

## A4. Хронология по дорожкам (обязательный навык)

Время на BPMN идёт **слева направо**. Дорожки — горизонтальные полосы ролей **внутри одного пула**. Мысленная сетка: вертикальные колонки общего времени на все `lane`.

**Передача другому участнику.** Sequence `-` на другую дорожку — это не «начать дорожку слева». Событие или задачу-приёмник ставьте **строго под источником** (тот же `x`, другая дорожка). Дальше по этой дорожке снова вправо. Независимые старты в разных дорожках по-прежнему слева.

```
pool shop {
  lane front {
    start go
    task greet user = Принять
  }
  lane back {
    task pack service = Собрать
    task ship service = Отгрузить
    end done
  }
}
go - greet - pack - ship - done
```

На холсте `pack` под `greet`, не под `go`. Редактор так раскладывает **новую** диаграмму (оверлей не пишите). Если просили подвинуть блоки — в оверлее не возвращайте приёмника к левому краю дорожки.

Поток на другую дорожку того же пула — **только `-`**, не `--`. `greet -- pack` внутри `shop` — **ошибка без coerce** (A6).

**Не путать с collaboration:** между пулами только `--`; выравнивание партнёров message — по вертикали, это не эта колонка времени.

**Один процесс — один пул.** Круговорот воды, цикл поставок, фазы одной системы — `lane` внутри одного `pool` и `-` между дорожками. Четыре `pool` с `--` по кругу (солнце → атмосфера → суша → океан → снова испарение) дают длинные возвратные стрелки и плохую картинку. Collaboration — когда это **разные организации**. Если `--` всё же возвращается к раннему старту другого пула — **не пишите оверлей**: редактор оставит старт на первом (левом) партнёре, а не под поздней задачей.

Время L→R действует и для **скетча без пула**: шаги в ряд, ветки шлюза — вертикальный веер.

## A5. Пулы, длина дорожек, внешние процессы

Соседние `pool` и `lane` **не накладываются** друг на друга: столчка сверху вниз, зазор между полосами. Длина всех свимлайнов **одинакова** и **достаточна**, чтобы все дочерние фигуры были внутри рамки (не обрезаны, не торчат за край).

**Хронология приоритетна.** Время идёт **слева направо**. Следующий этап процесса — правее предыдущего (или под партнёром message на своей полосе), не две задачи одного потока в одной клетке `x y`. Не жертвуйте порядком шагов ради «короткой» картинки.

**Побочный / внешний процесс — отдельный пул.** Отходы, очистка газов, утилизация, смежный завод — это процессы со своими `start` / `task` / `end` и потоками `-` внутри. Связь с основным процессом — `--` **от конкретной задачи** (не от рамки пула). Не моделируйте такой процесс висячим `data` и не пишите `-- НеобъявленныйId` без `pool { }`.

Не ставьте два пула в одну точку оверлея `(0, 0)`. Не сажайте две задачи одного sequence в одни `x y`. Новую диаграмму без просьбы про лейаут отдавайте **без** `SM:` — редактор сам выдержит столчку и длину.

## A6. `-` внутри пула, `--` только между пулами (ошибка без coerce)

Модели (в т.ч. после роли A4/A5) продолжают выдавать мельницу так: `qualityOk -- clean`, `pack -- store` внутри одного `pool plant`, плюс оверлей, где `labStart` под пробой, а `test` у левого края той же полосы. Свимлайн читается справа налево — это брак.

**Тире**

| Ситуация | Тире | Пример |
|----------|------|--------|
| Передача на другую `lane` того же завода | `-` | `qualityOk - clean` |
| Приёмка → мельница → склад в одном `pool` | `-` | `pack - store - ship` |
| Проба / разрешение в **другую** организацию | `--` | `sample -- labStart`, `labOk -- qualityOk` |
| `--` между задачами одного пула (не `data`/`store`) | **ошибка** | пишите `-`; парсер **не** coerce |
| `-` между разными пулами | **ошибка** | пишите `--`; парсер **не** coerce |

Лаборатория и ОТК — отдельные `pool`, не дорожки завода. Связь с заводом — `--` от конкретной задачи, не от рамки.

**Время в пуле только слева направо.** Старт — самый левый шаг своего процесса. Если процесс запускает `--` от партнёра, старт стоит **под отправителем**, а следующие шаги — **правее старта**, никогда у левого края полосы (`x=46`), пока старт правее. Единственный поток назад — петля (доразмол → рассев).

**Новую схему без оверлея.** Координаты `SM:` не угадывайте: битый оверлей сажает `test` левее `labStart` и ломает свимлайн. Редактор сам расставит. Оверлей — только если пользователь просил подвинуть блоки (Часть D).

**Решение — `xor`, не две задачи.** «Качество ОК / не ОК», «мука готова / на доразмол» — `xor quality { - clean да / - reject нет }`, не `task qualityOk` + `task qualityReject`. Цели веток можно объявлять **до или после** шлюза (`xor { - rain дождь }` затем `task rain` — один узел). Неизвестный id в ветке даст замечание. Не путайте stub `- outside Label` с другой `task real = Label`.

```
//smalum/bpmn Производство муки

pool plant {
  lane receive {
    start grainArrival
    task sample user = Отобрать пробу
    xor quality {
      - clean да
      - reject нет
    }
    task reject user = Вернуть поставщику
    end rejectEnd
  }
  lane mill {
    task clean service = Очистка зерна
    task grind service = Размол
    task pack service = Фасовка
  }
  lane warehouse {
    task store service = Принять на склад
    task ship service = Отгрузить
    end shipped
  }
}

pool laboratory {
  start labStart
  task test service = Анализ пробы
  xor labResult {
    - labOk годно
    - labFail брак
  }
  end labOk
  end labFail
}

grainArrival - sample - quality
reject - rejectEnd
clean - grind - pack - store - ship - shipped
sample -- labStart Проба
labStart - test - labResult
labOk -- quality Результат ОК
labFail -- reject Результат НЕ ОК
```

Не пишите `qualityOk -- clean` и не добавляйте `// SM:`.

---
