CI / ruff · pytest (pull_request) Successful in 22s
Freeze interfaces between lanes A (bot), B (backend), C (dashboard) so three developers work in parallel without touching each other's files: dialog Event/Reply + engine registry with echo stub, integration protocols with in-memory defaults, MonitoringService with noop stub, /api/v1 schemas with fixture-backed routes, frozen main.py wiring, config/modules.yaml + due_scales(), DAR-5, tzdata, Andrey trajectory, lanes in WORKFLOW, ADR-010. Refs #34 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
132 lines
8.1 KiB
Markdown
132 lines
8.1 KiB
Markdown
# Регламент работы: зоны, задачи, git, коммиты
|
||
|
||
> Основан на регламенте TenVex/vobot, облегчён под хакатон (ADR-009). Дедлайн: **пн 05.10.2026, 13:00 МСК**.
|
||
|
||
## Зоны (lanes) и файлы
|
||
|
||
Три разработчика делят **слои**, а не направления (ADR-010): каждый ведёт свой слой для всех сценариев. У каждой зоны свои файлы — в чужие не заходим, поэтому коллизий нет.
|
||
|
||
| Lane | Кто | Владеет файлами | Метки зон |
|
||
|---|---|---|---|
|
||
| **A — бот** | Егор (`Eleboba`) | `app/dialog/**`, `app/channels/**`, `app/llm/**`, `config/texts/**`, `tests/test_dialog_*`, `tests/test_telegram*`, `tests/test_llm*` | `bot`, `lane/A` |
|
||
| **B — backend** | разработчик 2 | `app/api/**` (кроме `schemas.py`), `app/services/**` (кроме `contracts.py`), `app/scheduler/**`, `app/integrations/**` (кроме `base.py`), `app/db/**`, `app/core/**` (правки — мелким PR), `scripts/**`, `data/**`, `tests/test_api*`, `tests/test_services*`, `tests/test_scheduler*`, `tests/test_seed*`, `tests/test_integrations*`, `tests/test_db*` | `backend`, `core`, `lane/B` |
|
||
| **C — дашборд** | разработчик 3 | `dashboard/**`, `tests/test_dashboard*` | `dashboard`, `lane/C` |
|
||
| clinical | психиатр | значения в `config/scales/*`, `config/rules.yaml`, `config/lexicon/*`, вычитка `config/texts/*` | `clinical` |
|
||
| pitch | медик | `docs/pitch/**` | `pitch` |
|
||
|
||
**Замороженные контракты** (меняются только отдельным мелким PR `[contract]` с пингом всех трёх):
|
||
`app/core/types.py`, `app/dialog/contracts.py`, `app/api/schemas.py`, `app/integrations/base.py`, `app/services/contracts.py`, `app/main.py`, `app/config.py`, `config/modules.yaml`, `config/rules.yaml` (схема), `pyproject.toml`/`uv.lock`, `docker-compose.yml`, `Makefile`, `.gitea/`.
|
||
|
||
**Как зоны стыкуются без ожидания друг друга:**
|
||
- A → B: движок вызывает `get_monitoring().after_data_change()` и `integrations.*`. До готовности B это заглушки (`NoopMonitoring`, `Memory*`).
|
||
- B → A: API и шедулер вызывают `get_engine().handle()`. До готовности A это `EchoEngine`.
|
||
- C → B: дашборд ходит только в `/api/v1`. До #12 ручки отдают фикстуры (`app/api/fixtures.py`).
|
||
- Реальная реализация регистрируется сама: `register_engine()` / `register_monitoring()` при импорте модуля, `integrations.<field> = ...` в `app/integrations/setup()`.
|
||
|
||
### Направления развития
|
||
| Направление | Статус | Milestone |
|
||
|---|---|---|
|
||
| Ветераны СВО | **фокус хакатона** | `Этап 1 · Ветераны СВО` (+ `M3 Сдача`) |
|
||
| Семьи ветеранов | опционально, после хакатона | `Будущее · Семьи ветеранов` (эпик #40) |
|
||
| Депрессия в общей популяции | опционально, после хакатона | `Будущее · Депрессия (общая популяция)` (эпик #43) |
|
||
|
||
Новое направление = новый файл `app/dialog/flows/<module>.py` + запись в `config/modules.yaml`; движок, API и дашборд расширяются в своих зонах.
|
||
|
||
---
|
||
|
||
## Откуда задачи
|
||
|
||
- Только из Gitea `$GITEA_REPO`. Нет issue — нет кода. Нет подходящего issue — заведи его на себя.
|
||
- Атрибуты: **метка зоны** + `lane/A|B|C`, **тип** (`feature`/`fix`/`refactor`/`docs`/`chore`), **размер** (`size/0.5` ≈ 2 ч, `size/1` ≈ 4 ч, `size/2` ≈ день), **milestone** (M1/M2/M3), приоритет `P0`/`P1`/`P2`.
|
||
- **Одна задача = одна зона.** Нужно несколько зон → несколько issue, связанных зависимостью через API ([`GITEA_API.md`](GITEA_API.md)).
|
||
- Тело issue — по шаблону [`ISSUE_TEMPLATE.md`](ISSUE_TEMPLATE.md).
|
||
|
||
### Milestones
|
||
| Milestone | Срок | Смысл |
|
||
|---|---|---|
|
||
| M1 Каркас | сб 03.10 ✅ | ядро, БД, LLM-шлюз, конфиги |
|
||
| Этап 1 · Ветераны СВО | вс 04.10, 23:59 | демо Андрея end-to-end (эпик #35) |
|
||
| M3 Сдача | пн 05.10, 13:00 | README, презентация PDF, репетиция, `dev → main` |
|
||
| Будущее · … | без срока | опциональные направления |
|
||
|
||
---
|
||
|
||
## Жизненный цикл задачи
|
||
|
||
```
|
||
1. Взял issue (assignee = $GITEA_USER), зона свободна
|
||
2. git fetch origin
|
||
3. git worktree add .claude/worktrees/<issue#>-<slug> -b <type>/<issue#>-<slug> origin/dev ← ДО первого изменения
|
||
4. cp .env в worktree; uv sync
|
||
5. Код по модулям; коммиты с Refs #N
|
||
6. git ls-remote --heads origin <ветка> ← пусто после первого пуша = PR смержен, стоп
|
||
7. git fetch && git rebase origin/dev
|
||
8. make lint && make test
|
||
9. Лог в тело issue: ### YYYY-MM-DD
|
||
10. PR в dev с Closes #N → после зелёного CI мержит автор (облегчение хакатона)
|
||
11. git worktree remove .claude/worktrees/<issue#>-<slug>
|
||
```
|
||
|
||
**Задача завершена → сам: коммит → push → PR.** Не запушено — не сделано.
|
||
|
||
## Git
|
||
|
||
```
|
||
main ← сдаваемая версия, мержит только человек (пн утром)
|
||
dev ← интеграция, сюда все PR
|
||
<type>/<issue#>-<slug> ← от dev
|
||
```
|
||
|
||
## Коммиты — Conventional Commits
|
||
```
|
||
feat(core): add PHQ-9 scoring with item-9 escalation
|
||
|
||
Refs #3
|
||
```
|
||
scope = зона. `Closes #N` — только в описании PR.
|
||
|
||
## Шаблон PR
|
||
```markdown
|
||
## Что меняется
|
||
1–3 предложения.
|
||
|
||
Closes #<N>
|
||
|
||
## Зона
|
||
A | B | C | clinical | pitch | docs | contract
|
||
|
||
## Как проверить
|
||
шаги/команды
|
||
|
||
## Чек-лист
|
||
- [ ] rebase на origin/dev
|
||
- [ ] make lint + make test зелёные
|
||
- [ ] лог в issue (### YYYY-MM-DD)
|
||
- [ ] нет ПДн в логах, нет реальных данных, нет секретов
|
||
- [ ] общие файлы (CLAUDE.md, docker-compose, pyproject) — отдельный мелкий PR с пингом
|
||
```
|
||
|
||
---
|
||
|
||
## Guardrails (проверяются в каждом PR)
|
||
|
||
1. **В LLM — только `scrub()`-текст.** Прямой вызов клиента GigaChat в обход `app/llm/gateway.py` запрещён.
|
||
2. **Safety до LLM.** При `acute` LLM не вызывается вообще.
|
||
3. **Алерты и скоринг детерминированы** — LLM не решает (ADR-007).
|
||
4. **Время — только из `app.core.clock`** (ADR-005).
|
||
5. **identity ↔ clinical — только через токен.** Модули clinical не импортируют `app.db.identity`.
|
||
6. **Доступ врача → `audit_log`.**
|
||
7. **Пороги, тексты и лексикон — в `config/`**, не в коде.
|
||
8. **Только синтетика.** Логи без ПДн и без сырого текста.
|
||
9. **Никаких зарубежных API в контуре.**
|
||
|
||
## Факапы
|
||
|
||
| Ситуация | Действие |
|
||
|---|---|
|
||
| Начал в основной копии | `git stash` → worktree → `git stash pop` |
|
||
| `git push` дал `[new branch]` после мержа | удалить ветку через API, новый issue + cherry-pick |
|
||
| Конфликт при rebase в чужой зоне | спросить владельца |
|
||
| Утёк секрет | отозвать ключ, вычистить историю |
|
||
| Битая кириллица в issue/PR | см. [`GITEA_API.md`](GITEA_API.md): только `--data-binary @файл` |
|