# EventHub — правила работы Живой документ процесса разработки. Дополняется по мере появления новых договорённостей. **Репозитории:** `EventHubBack`, `EventHubFrontAdmin`, `EventHubSpec` **Трекер:** https://git.sabilin.com/EventHub **Cursor skill для задач:** `eventhub-gitea` --- ## 1. Задачи (Gitea) ### Язык Все задачи, комментарии и описания — **на русском**. Исключение: имена файлов, пути API, идентификаторы в backticks. ### Жизненный цикл | Этап | Действие | |------|----------| | Старт | Прочитать issue → назначить на себя → комментарий «Беру в работу» | | План | **Предложить 1–3 варианта решения с trade-offs → ждать утверждения** | | Работа | После утверждения — код в репозитории-владельце; коммиты с `Refs EventHub/#N` | | Финиш | Тесты зелёные → комментарий с итогом → закрыть issue | ### Метки (единый набор во всех репо) | Метка | Назначение | |-------|------------| | Bug | Дефект | | Task | Техническая задача | | Story | Пользовательская история | | Epic | Крупная инициатива | | Future | Отложено | Синхронизация меток: `~/.cursor/skills/eventhub-gitea/scripts/sync-labels.ps1` ### Приоритеты `P0` → `P1` → `P2` (указывается в заголовке задачи). --- ## 2. Тестирование (обязательный gate) **Нельзя** закрывать задачу и **нельзя** обновлять спеку, пока: - пройдены релевантные тесты репозитория; - выполнена ручная проверка, если автотестов нет; - нет известных регрессий в затронутой области. | Репозиторий | Минимум | |-------------|---------| | EventHubBack | `make test` / `make eunit` / целевые API-тесты (см. §2.1 WSL) | | EventHubFrontAdmin | `npm run lint`, `npm run build`, **`npm run test:e2e`** (Playwright mock-suite) | | EventHubSpec | ревью текста, ссылки на код | IFT smoke Admin UI (живой API после деплоя): `npm run test:e2e:ift` / workflow `e2e-ift.yml`. Учётки — из env стенда (`SMOKE_ADMIN_*` / `ADMIN_SUPER_*` в `EventHubDevOps/{ift,stage}/.env`). Контракт селекторов: `EventHubFrontAdmin/e2e/TESTIDS.md`. --- ## 2.1. Локальная разработка (WSL) Окружение: **WSL + asdf** (Erlang/OTP 28, rebar3). PowerShell/Windows — только IDE; сборка и тесты — в WSL. ```bash cd /mnt/c/Users/alexc/IdeaProjects/eventHub/EventHubBack bash scripts/wsl-ext4-dirs.sh # один раз: logs/data на ext4 bash scripts/fix-rebar-unit-symlink.sh # перед CT, если rebar сломал unit/api rebar3 as test eunit rebar3 ct --suite=api_admins_SUITE ``` ### Git-коммиты из WSL (Agent и разработчик) Коммиты из **Agent Shell / PowerShell** ломаются: Cursor подставляет `--trailer "Co-authored-by: Cursor "`, а PowerShell падает на `<`. **Решение:** Agent коммитит через WSL-скрипт в `~/.cursor/eventhub/` (в команде нет строки `git commit`): ```bash EH=/mnt/c/Users/alexc/IdeaProjects/eventHub GIT=/mnt/c/Users/alexc/.cursor/eventhub bash $GIT/git-commit.sh $EH/EventHubBack \ "сообщение. Refs EventHub/EventHubBack#N" \ path/to/file ... ``` Ручной коммит — тот же вызов в WSL-терминале. ### Пропуск CI (Gitea Actions) В **последнем** коммите пуша (или в title PR) можно отключить запуск workflow: | Маркер | Пример | |--------|--------| | `[skip ci]` | предпочтительно | | `[ci skip]` / `[no ci]` / `[skip actions]` / `[actions skip]` | синонимы | Gitea не ставит run в очередь → не сработают deploy по `workflow_run`. Через скрипт: ```bash SKIP_CI=1 bash $GIT/git-commit.sh $EH/EventHubBack \ "docs only. Refs EventHub/EventHubBack#N" \ path/to/file # или bash $GIT/git-commit.sh $EH/EventHubBack "docs" file.md --skip-ci ``` Не злоупотреблять: без CI нет образов/IFT. Ручной `workflow_dispatch` по-прежнему доступен. ### Push на remote **Agent делает `git push` только после явного подтверждения пользователя** («пуш», «отправь», «push»): ```bash bash $GIT/git-push.sh $EH/EventHubBack ``` До подтверждения: коммиты остаются локально; в конце задачи Agent напоминает, что можно пушить. Attribution в Cursor (Settings → Agent → Attribution) можно выключить дополнительно. --- ## 3. Спецификация (после успешных тестов) Если изменение затрагивает **поведение, API, модель данных или роли**: 1. Обновить соответствующий файл в `EventHubSpec/`: - backend → `EventHubBackSpec.md` - admin → `EventHubFrontAdminSpec.md` 2. В комментарии к issue указать: «Спека обновлена: …» 3. Спека описывает **фактическую** реализацию, не планы. Что синхронизировать: - endpoint-ы (путь, метод, auth); - форматы запросов/ответов; - поля records/таблиц; - роли и права; - ограничения и допущения. Чистый рефакторинг без смены контракта — обновление спеки не обязательно (написать в issue). --- ## 5. Старт нового чата (передача контекста) Новую задачу лучше начинать в **отдельном чате** с коротким брифом — не копировать всю переписку. ### Что приложить в Cursor - `@EventHubSpec/WORKFLOW.md` — этот документ - Ссылка на issue: `https://git.sabilin.com/EventHub//issues/N` - При необходимости: 1–3 ключевых файла кода (`@src/...`) ### Шаблон первого сообщения ```markdown Проект: EventHub - EventHubBack: .../EventHubBack - EventHubFrontAdmin: .../EventHubFrontAdmin - EventHubSpec: .../EventHubSpec Gitea: https://git.sabilin.com/EventHub Следуй WORKFLOW.md и skills eventhub-gitea / eventhub-workflow. Окружение: WSL + asdf (OTP 28). Git: ~/.cursor/eventhub/git-commit.sh, push — только после «пуш». Перед CT: scripts/fix-rebar-unit-symlink.sh; logs/data: scripts/wsl-ext4-dirs.sh Задача: EventHubBack#24 — <краткая цель> Issue: https://git.sabilin.com/EventHub/EventHubBack/issues/24 Контекст (что уже сделано): - #23 закрыт: auth_session, admin refresh JWT (фаза 1) Правила: 1–3 варианта → утверждение → код; тесты перед закрытием; спека после контракта. ``` ### Минимальный вариант (одной строкой) > Берём EventHubBack#24. WORKFLOW.md. WSL, git через ~/.cursor/eventhub/. Варианты — потом утверждение. Пуш — только когда скажу. ### Что не передавать - Полные логи, diff, историю старого чата - Токены (`GITEA_TOKEN` — в `~/.cursor/secrets/eventhub-gitea.env`) --- ## 6. Версии и деплой (IFT / stage) ### Product version (`VERSION`) В корне `EventHubBack` и `EventHubFrontAdmin` лежит файл **`VERSION`** в формате **`MAJOR.MINOR`** (сейчас `0.1`). - Бампается **вручную и редко** (релиз продуктовой линейки), не на каждый push. - Patch-уровень SemVer **не** используется для stage-деплоев. - Ручные git-теги `v*` для выкладки на stage **не** нужны. ### Идентичность сборки | Поле | Откуда | Назначение | |------|--------|------------| | product version | `VERSION` | «линейка» продукта | | git sha | `GITHUB_SHA` (12 символов) | точная сборка | | built_at | время CI UTC | диагностика | - **FrontAdmin:** bake в Vite (`VITE_APP_VERSION`, `VITE_GIT_SHA`, `VITE_BUILT_AT`) на шаге CI. - **Back:** bake в Docker image (`EVENTHUB_VERSION`, `EVENTHUB_GIT_SHA`, `EVENTHUB_BUILT_AT`) → `GET /admin/health` и `GET /v1/admin/health` (`version`, `git_sha`, `built_at`). - В UI (меню профиля): `Admin 0.1 (sha)` + `API 0.1 (sha)`. ### Registry / окружения | Окружение | Тег образа | Когда | |-----------|------------|--------| | IFT / push CI | `sha-<12>` + floating `:ift` / `:dev` | после зелёного CI | | Stage | тот же `sha-<12>` + floating `:stage` | auto после успешного CI на `master` (`deploy-stage.yml` → promote + SSH deploy) | IFT и stage крутят **одну и ту же** сборку `sha-*`. Product version из `VERSION` на деплой не влияет. ### E2E Admin UI (Playwright) - **CI / PR:** `npm run test:e2e` — Chromium, API-моки (`e2e/mocks`), project `mock`. - **IFT smoke:** после успешного Deploy IFT — workflow `e2e-ift.yml` → `npm run test:e2e:ift` (без моков). Учётки с хоста IFT: `/opt/eventhub-ift/.env` (`SMOKE_ADMIN_*` → `ADMIN_SUPER_*`). - Селекторы: `data-testid` + a11y; контракт в `EventHubFrontAdmin/e2e/TESTIDS.md`. --- ## 7. Нагрузочное тестирование IFT (Tsung) Стенд IFT (HOME-PC): Docker Swarm **внутри WSL2**. Часть BOUNDARY_HIT под aggressive stress — артефакт гипервизора/`vmmem`, не только приложения. ### Baseline (июль 2026, текущее железо) | Метрика | Значение | |---------|----------| | Рабочий потолок hot-path (до пересчёта) | ~**10 VU/s** (interarrival ≥ 0.1 s, thinktime ≥ 1 s), прогон ×3 + WAF | | Green hold | фазы 0.2 / 0.1 s | | OVER (connect) | ~20 VU/s (interarrival 0.05) на ×1/×2 (исторически) | | Пересчёт | после fix Mnesia dump_log + Traefik loadtest без WAF: лестница **×1 → ×2 → ×3** | Подробности: `EventHubBack/test/tsung/README.md`, задача `EventHubBack#30`. ### Правила (обязательно) - **Default stress-профиль:** `eventhub_ift_stress_hold.xml` / `stress-hold`. - `maxusers` ≤ **300** (для `stress-hold` default **100**); `thinktime` ≥ **1** s; плановый interarrival ≥ **0.1** s. - Uncapped / `stress-bump` / `maxusers≫300` на IFT **запрещены** без явного согласования (`CONFIRM_DANGEROUS=1` в оркестраторе). - Prepare/restore: `EventHubDevOps/scripts/ift-loadtest-*.sh`; при hang WSL SSH — Windows `restore-ift.ps1` / `wsl --shutdown`. - **Потолок vs репликация:** сначала `EVENTHUB_REPLICAS=1`, затем 2, 3 — отдельные прогоны с gate; не смешивать ступени. - Prepare по умолчанию подменяет Traefik на `dynamic_conf.loadtest.yml` (**без WAF**); `LOADTEST_TRAEFIK=0` — штатный conf с Coraza. ### После каждого stress-прогона (обязательно) Прогон **не считается завершённым**, пока не сделаны сбор, разбор и **очистка дампов на IFT**. Restore без архива — только если стенд уже мёртв и сбор невозможен; тогда collect сразу после `wsl --shutdown`. 1. **Tsung summary (2A):** `python3 test/tsung/summarize-tsung-stress.py ~/.tsung/log//tsung.log` → `verdict`, error_rate, mean, коды HTTP, transport errors. 2. **Сбор логов стенда** на exchange-share (`\\…\eventhub-cursor\` / `C:\eventhub-cursor-exchange\ift-to-dev\loadtest-\`): - SSH жив: `collect-ift-loadtest-logs.ps1` (после download сам чистит remote dump этого stamp) - SSH banner hang: `restart-wsl-and-collect-logs.ps1` **или** вручную `wsl --shutdown` на HOME-PC → затем `collect-ift-loadtest-logs.ps1` - На консоли IFT без SSH: `collect-ift-loadtest-logs-on-ift.sh` (после copy на шару чистит локальные дампы) 3. **Анализ (минимум):** - сравнить с предыдущим прогоном того же профиля/replicas; - в архиве: `eventhub-service.log` / `traefik-service.log` — 429, Mnesia `dump_log`, archive/peer, OOM, crash; - `ift-docker-stats-*.log` / `docker-stats.txt` — CPU/MEM пики; - **метрики EventHub (обязательно смотреть):** - scrape `GET https://api.ift.eventhub.local/metrics` (Prometheus text, без auth; eventhub остаётся up в prepare) — до/во время/после; оркестратор пишет сэмплы в `~/.tsung/ift-eh-metrics-*.log`; - история узлов: `GET /v1/admin/nodes/metrics?from=&to=` (admin JWT) — переживает restart, если volume Mnesia цел; - стек Grafana/Prometheus (compose) при `KEEP_OBSERVABILITY=1` — опционально; если prepare гасит их, mid-run там пусто; - зафиксировать: образ `sha-*`, replicas, verdict, узкое место. 4. **Очистка дампов на IFT** (если collect не очистил / остались старые): `cleanup-ift-loadtest-dumps.ps1` / `EventHubDevOps/scripts/cleanup-ift-loadtest-dumps.sh` Удаляет `~/loadtest-logs-*`, `/tmp/ift-loadtest-logs-*.tgz`, `/tmp/ift-collect-*.sh`. **Копии на шаре и Tsung на дев не трогает.** 5. **Restore** полного стека (`ift-loadtest-prepare` уже гасит observability; restore поднимает) + smoke health. 6. **Отчёт:** комментарий в issue (`EventHubBack#30`) и/или строка в `EventHubBack/test/tsung/README.md`. Prepare: по умолчанию compose prometheus/grafana выключены. Для скрейпа **приложения** это не нужно — `/metrics` на eventhub доступен через Traefik всегда, пока up нода. `KEEP_OBSERVABILITY=1` — только если нужен отдельный Prometheus/Grafana стек. ### Критерий верхней границы (2A) error rate > 1% **или** mean request > 500 ms (hold ≥2 мин в фазе) → `BOUNDARY_HIT`. Сравнение hold на bare Linux (без WSL2) — отложено: `EventHubDevOps#4` (Future). --- ## 4. Расширение правил При появлении новой договорённости в работе: 1. Добавить пункт в этот файл (раздел «Правила» ниже). 2. При необходимости — задача `Task` в `EventHubSpec` на согласование. 3. Для Cursor — обновить skill `eventhub-workflow`. --- ## Правила (накопительный список) - Задачи в Gitea вести через skill `eventhub-gitea`; тексты на русском. - **Перед реализацией** — предложить оптимальные варианты решения; кодить только после утверждения варианта пользователем. - Перед закрытием issue — успешные тесты изменённого кода. - После успешных тестов — обновить спеку, если менялся внешний контракт. - Одна задача — один логический объём работы. - Код фичи — в репозитории-владельце; `EventHubSpec` — для документации и процесса. - Метки во всех репозиториях держать одинаковыми (`sync-labels.ps1`). - Секреты и токены не коммитить; Gitea token — только в env / `~/.cursor/secrets/`. - **Git commit/push — через WSL** (`~/.cursor/eventhub/git-commit.sh`, `git-push.sh`); **push — только после подтверждения пользователя** (см. §2.1). - **Пропуск CI:** `[skip ci]` (и синонимы) в сообщении последнего коммита пуша / title PR; `SKIP_CI=1` или `--skip-ci` в `git-commit.sh` (см. §2.1). - Новая задача — **отдельный чат** с брифом по §5. - **Версии/деплой:** product = `VERSION` (`MAJOR.MINOR`); IFT+stage = `sha-*` (+ floating `:ift`/`:stage`); без ручных stage-тегов и без patch++ на каждый push (см. §6). - **FrontAdmin E2E:** mock-suite обязателен в CI; IFT smoke — после Deploy IFT (`e2e-ift.yml`, учётки из стендового `.env`). - **IFT loadtest:** hold-профиль default; потолок пересчитывать лестницей ×1→×2→×3; Traefik loadtest без WAF; uncapped stress запрещён; после stress — collect+анализ (+мониторинг)+**cleanup дампов на IFT**+отчёт+restore (см. §7). --- ## История | Дата | Изменение | |------|-----------| | 2026-07-15 | §7: анализ через EventHub `/metrics` + admin `nodes/metrics`; compose Prom опционален | | 2026-07-15 | §7: cleanup дампов на IFT + мониторинг в анализе; KEEP_OBSERVABILITY | | 2026-07-15 | §7: сбор и анализ логов — обязательный gate после stress | | 2026-07-15 | §7: capacity baseline IFT + правила Tsung stress (после EventHubBack#30) | | 2026-07-15 | §2.1: пропуск CI — `[skip ci]` / `SKIP_CI=1` в git-commit.sh | | 2026-07-14 | §6 + gate: Playwright E2E (mock CI + IFT smoke secrets) | | 2026-07-14 | §6: VERSION MAJOR.MINOR + auto stage по sha после CI | | 2026-07-07 | Git-скрипты Agent перенесены в `~/.cursor/eventhub/` (не в репозиториях) | | 2026-07-07 | §5: шаблон передачи контекста в новый чат | | 2026-07-07 | Push на remote — только после подтверждения пользователя | | 2026-07-07 | §2.1: WSL, git-коммиты через `scripts/git-commit.sh` | | 2026-07-07 | Правило: варианты решения и утверждение перед реализацией | | 2026-07-07 | Первая версия: Gitea workflow, gate тестирования, sync спеки |