Files
EventHubSpec/WORKFLOW.md
T

336 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EventHub — правила работы
Живой документ процесса разработки. Дополняется по мере появления новых договорённостей.
**Репозитории:** `EventHubBack`, `EventHubFrontAdmin`, `EventHubSpec`
**Трекер:** https://git.sabilin.com/EventHub
**Cursor skill для задач:** `eventhub-gitea`
---
## 1. Задачи (Gitea)
### Язык
Все задачи, комментарии и описания — **на русском**.
Исключение: имена файлов, пути API, идентификаторы в backticks.
### Жизненный цикл
| Этап | Действие |
|------|----------|
| Старт | Прочитать issue → назначить на себя → комментарий «Беру в работу» |
| План | **Предложить 1–3 варианта решения с trade-offs → ждать утверждения** |
| Работа | После утверждения — код в репозитории-владельце; коммиты с `Refs EventHub/<repo>#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 <cursoragent@cursor.com>"`, а 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/<Repo>/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, IFT / WSL2, `sha-6d8f02d`)
| Метрика | Значение |
|---------|----------|
| Зелёный hold (×2 / ×3, maxusers=100) | 0 errors; mean ~4065 ms; late ≤ ~210 ms |
| ×1, maxusers=100 | **BOUNDARY** по latency (mean ~613 ms) — одной ноды мало |
| Hot ladder ×3 (0.1→0.05→0.035, maxusers=100) | **UNDER** (mean ~47 ms) |
| Soft OVER concurrent | maxusers=**150** на ×3 — 0 errors, но late ~1.5 s → BOUNDARY |
| Prod-ориентир с IFT | минимум **2** реплики EventHub; абсолютные VU/CPU с WSL **не** копировать 1:1 |
Подробности: `EventHubBack/test/tsung/README.md`. Абсолютный потолок на bare Linux — `EventHubDevOps#4` (Future).
### Правила (обязательно)
- **Default stress-профиль:** `eventhub_ift_stress_hold.xml` / `stress-hold` (maxusers=**100**).
- `maxusers`**150** на IFT без согласования; **200+** / uncapped / `stress-bump` / beams — только с `CONFIRM_DANGEROUS=1`.
- `thinktime`**1** s; плановый interarrival ≥ **0.1** s для green hold.
- Prepare/restore: `EventHubDevOps/scripts/ift-loadtest-*.sh`; при hang WSL SSH — Windows `restore-ift.ps1` / `wsl --shutdown`.
- **Потолок vs репликация:** сначала `EVENTHUB_REPLICAS=1`, затем 2, 3 — отдельные прогоны с gate.
- IFT Traefik: штатно **без Coraza/WAF** (память); loadtest-оверлей — `dynamic_conf.loadtest.yml` / `LOADTEST_TRAEFIK`.
### После каждого stress-прогона (обязательно)
Прогон **не считается завершённым**, пока не сделаны сбор, разбор и **очистка дампов на IFT**. Restore без архива — только если стенд уже мёртв и сбор невозможен; тогда collect сразу после `wsl --shutdown`.
1. **Tsung summary (2A):** `python3 test/tsung/summarize-tsung-stress.py ~/.tsung/log/<stamp>/tsung.log``verdict`, error_rate, mean, коды HTTP, transport errors.
2. **Сбор логов стенда** на exchange-share (`\\…\eventhub-cursor\` / `C:\eventhub-cursor-exchange\ift-to-dev\loadtest-<stamp>\`):
- 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 maxusers=100; зелень на ×2/×3; ×1 — latency BOUNDARY; absolute capacity — bare Linux (`EventHubDevOps#4`); uncapped/beams без `CONFIRM_DANGEROUS=1` запрещены; после stress — collect+анализ+cleanup+отчёт+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 спеки |