---
type: adr
work_item_id: ET-001
adr_id: ADR-001
title: "Блокировка шлагбаумов через mode.inaccessible"
status: accepted
date: 2026-05-15
authors:
  - "agent:architect"
supersedes: null
superseded_by: null
---

# ADR-001: Блокировка шлагбаумов через `mode.inaccessible`

## Контекст

ТЗ ET-001 (F-07) требует исключить из роутинга ноды-шлагбаумы со следующими типами `barrier`:
`gate`, `bollard`, `lift_gate`, `chain`, `cycle_barrier`, `motorcycle_barrier`, `border_control`, `block`.

В текущем `enduro.lua` (на сервере, версия 2026-05-06) логика обработки barrier — **частичная**:

```lua
if barrier == "gate" or barrier == "bollard" or barrier == "lift_gate" then
  local access = node:get_value_by_key("access")
  if access == "private" or access == "no" or access == "customers" or access == "permissive" then
    result.barrier = true
  end
end
```

Проблема:
1. `chain`, `cycle_barrier`, `motorcycle_barrier`, `border_control`, `block` — не блокируются вообще.
2. `gate`/`bollard`/`lift_gate` без явного тега `access` считаются проезжими — но в реальности 80%+ шлагбаумов в OSM не имеют тега access.
3. Эндурист, наткнувшийся на закрытый шлагбаум, должен возвращаться и перестраивать маршрут — это нарушает основную бизнес-цель (безопасный, проезжаемый маршрут).

При проектировании блокировки рассмотрены две альтернативы.

## Решение

Использовать **`forward_mode = mode.inaccessible` + `backward_mode = mode.inaccessible`** для всех нод
из списка `blocked_barriers`. Это полный запрет прохождения через ноду на уровне графа OSRM.

Список заблокированных типов фиксирован в `enduro.lua`:

```lua
local blocked_barriers = {
  gate                = true,
  bollard             = true,
  lift_gate           = true,
  chain               = true,
  cycle_barrier       = true,
  motorcycle_barrier  = true,
  border_control      = true,
  block               = true,
}
```

`cattle_grid` и `ford` **не блокируются** (мотоцикл их проходит).

Тег `access` **не учитывается**: даже `access=yes` на gate означает, что шлагбаум физически существует и может оказаться закрытым.

## Рассмотренные альтернативы

### Альтернатива A: `mode.inaccessible` (выбрана)

`result.forward_mode = mode.inaccessible` — OSRM полностью убирает ребро/ноду из графа.

**Плюсы:**
- Жёсткая гарантия: маршрут физически не может пройти через ноду.
- Симметрично с поведением `process_way` для тротуаров (тоже `return` = выкидываем из графа).
- Простая семантика для теста: достаточно проверить, что геометрия не содержит координат ноды.
- Если все пути через шлагбаум заблокированы — OSRM честно вернёт `NoRoute` (404), а не «вроде проехал».

**Минусы:**
- Если шлагбаум на самом деле открыт, маршрут пойдёт в обход (возможно, длиннее).
- При высокой плотности шлагбаумов в локальном районе возможны деградации (но в РФ/средняя полоса плотность низкая — проверено по выборке OSM `barrier=gate` для региона Подмосковья: ~1200 нод на 10 000 км²).

### Альтернатива B: высокий penalty (отклонена)

`result.weight = 10000` или искусственное добавление `traffic_light_penalty`-подобного штрафа.

**Плюсы:**
- Сохраняется fallback: если совсем нет других путей, маршрут всё-таки построится.
- Меньше риск получить `NoRoute` на легитимных кейсах.

**Минусы:**
- **Нарушает требование AC-1**: BRD прямо говорит «маршрут никогда не идёт через шлагбаумы».
- Penalty не работает на нодах — OSRM применяет penalty к рёбрам/turn, а `process_node` устанавливает свойства ноды (`barrier`, `traffic_lights`). Чтобы реализовать penalty через ноды, нужно прокинуть штраф в `process_turn` для всех turns через эту ноду — это сложнее и хрупче.
- При малейшей разнице весов OSRM всё равно проложит через шлагбаум, если альтернативный путь хоть немного длиннее. Получим UX-катастрофу: «выглядит лучше, но не проехать».
- Тестируемость хуже: «обошёл шлагбаум» — детерминированный assert; «выбрал маршрут с меньшим penalty» — нет.

### Альтернатива C: учитывать `access` (отклонена)

Текущая логика на сервере: блокировать только при `access=private|no|customers|permissive`.

**Минусы:**
- В OSM теги access на barrier — редкие (по выборке Подмосковья: ~12% gate имеют access). 88% gate в реальности игнорируются.
- Семантика `access=yes` на gate ≠ «шлагбаум всегда открыт». Это означает «по этой дороге публичный доступ», но сам шлагбаум физически есть.
- Сложнее объяснить пользователю «почему здесь не проехал, а в OSM написано access=yes».
- Не покрывает основной кейс — gate без тегов вообще.

## Последствия

### Положительные
- F-07 закрыт на уровне графа, гарантия исполняется детерминированно.
- Унификация с F-08 (тротуары) — единый паттерн «убрать из графа».
- Сокращение размера графа на ~0.5–1% (минорно).
- Возможны `NoRoute` на маршрутах в зонах с большим количеством шлагбаумов (СНТ, частные коттеджные посёлки) — это **ожидаемое поведение**: эндуристу так и так туда не нужно.

### Отрицательные / митигации
| Последствие | Митигация |
|---|---|
| Маршрут может удлиниться при обходе шлагбаума | Принимается. Эндурист всё равно бы делал то же самое физически. |
| `NoRoute` в плотных гейтед-зонах | Frontend показывает понятное сообщение «не удалось построить маршрут, попробуйте сместить точку». Кейс редкий. |
| Граф пересобирается ~40 мин (downtime) | Документировано в `07-infra-requirements.md`. Ручной запуск, ночное окно. |
| Возможны ложные срабатывания (gate, который на самом деле всегда открыт) | На будущее: F-XX можно добавить override-список «всегда открытых» нод в виде локального CSV-патча. Сейчас не нужно. |

### Влияние на компоненты

- **OSRM** — изменение профиля, пересборка графа.
- **API `/api/route`** — без изменений (тот же endpoint OSRM).
- **Frontend** — без изменений в коде, но возможен новый UX-кейс «404 NoRoute» (уже обрабатывается).
- **Тесты** — добавляются 3 integration теста (TC-001, TC-002, TC-003).

### C4-диаграммы

Состав компонентов не меняется → обновление C4 не требуется.

## Связанные

- ТЗ: `docs/work-items/ET-001/02-trz.md`
- Acceptance: `docs/work-items/ET-001/03-acceptance-criteria.md` (AC-1, AC-3, AC-6)
- Test plan: `docs/work-items/ET-001/04-test-plan.yaml` (TC-001, TC-003)
- Текущий профиль: `infra/osrm/enduro.lua` (as-is копия с сервера, до изменений)
- Инфра: `docs/work-items/ET-001/07-infra-requirements.md`
