Правила работы — `AGENTS.md` в корне репозитория.

Репозиторий: `https://github.com/ochenstarik-ui/kagent`, база — свежий `main`
Ветка: новая от `origin/main`, `wt/c5-nats-events`

**Зависимостей нет.** Задача трогает Python-сервисы и конфигурацию, с c1 и c4 по файлам не
пересекается и может идти параллельно.

## Проблема

`services/nats/src/events.py` — клиент JetStream: подключение, публикация доменного
события, подписка durable-консьюмером, запрос-ответ. Модуль не импортируется ни одним
сервисом и попал в `docs/known-drift.json`.

Кроме того, он не заработал бы и при подключении: `subscribe()` вызывает `pull_subscribe` с
именем потока, который нигде не создаётся. Вызова `add_stream` в модуле нет. Первая же
подписка упадёт на отсутствующем потоке.

Каталог `services/nats` при этом не является сервисом: в нём нет ни `requirements.txt`, ни
`Dockerfile`, ни записи в `docker-compose.yml`. Это библиотека, лежащая в каталоге сервисов.

## Что сделать

### 1. Починить клиент

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

### 2. Переместить как библиотеку

`services/nats` — не сервис. Перенести модуль туда, где он является общей библиотекой
Python-сервисов, например `packages/py-events` либо `services/_shared`, и подключить как
зависимость к использующим сервисам. Выбранный вариант обосновать одной строкой в отчёте:
структура каталогов описана в ТЗ, раздел 7, и не должна расходиться с фактом.

### 3. Подключить к одному сервису

Достаточно одного настоящего потребителя, чтобы возможность считалась подключённой.
Выбрать `services/pipeline`: он уже выполняет шаги и знает об их результате.

Публиковать доменные события жизненного цикла шага с именами из ТЗ, раздел 10:
`task.started`, `agent.started`, `agent.completed`, `artifact.created`, `task.failed`.
Событие содержит идентификатор, тип, версию схемы, время, идентификаторы проекта и задачи,
корреляционный идентификатор — как требует ADR-0002.

- адрес брокера из `NATS_URL`, значение по умолчанию и запись в `.env.example`;
- `nats-py` добавить в `requirements.txt` сервиса; версия уже используется в
  `services/orchestrator`, взять ту же;
- в `docker-compose.yml` добавить зависимость `pipeline` от `nats` по состоянию
  работоспособности;
- **недоступность брокера не должна ронять пайплайн.** Публикация — побочный эффект, а не
  условие выполнения шага. При недоступности брокера событие теряется с записью в лог, шаг
  продолжается. Это осознанный выбор на текущем этапе: гарантированная доставка требует
  outbox из ADR-0010, которого ещё нет. Отметить это в отчёте.

### 4. Доказательство

Модульные тесты на заглушке брокера: событие сериализуется с обязательными полями, имя
потока вычисляется одинаково для публикации и подписки, недоступность брокера не прерывает
работу.

Интеграционный тест с настоящим брокером в непрерывной интеграции: сервисный контейнер
`nats:2.11-alpine` с включённым JetStream, публикация и получение события подписчиком,
повторное создание потока не ломает работу.

Добавить возможность в `docs/capabilities.json` со ссылкой на имя нового job как на
доказательство и снять запись из `docs/known-drift.json`.

## Чего не делать

- публикация событий из Control Plane на TypeScript — отдельная задача, там понадобится
  своя реализация клиента;
- outbox и гарантированная доставка — ADR-0010, отдельный этап;
- подключение оркестратора: он вообще отсутствует в `docker-compose.yml`, это отдельная
  проблема.

## Критерий приёмки

```bash
ruff check services scripts
python -m pytest tests/unit -q
python scripts/drift_check.py
```

- модуль импортируется настоящим сервисом, а не только тестом;
- `drift_check.py` не сообщает про модуль событий; в `known-drift.json` осталось две записи
  либо одна, если c3 уже принята;
- job с брокером в CI зелёный, интеграционный тест публикует и получает событие;
- `roadmap_status.py` показывает возможность событийного потока подтверждённой
  доказательством — вывод до и после в отчёте;
- ссылка на зелёный прогон приложена.
