На защите (docs/DEMO.md) хотим, чтобы любой из зала отсканировал один QR с экрана и попробовал бота. Сейчас приглашение одноразовое (SPEC A-3, ADR-011): первый, кто отсканирует, его «забирает», остальные получают отказ. Нужно многоразовое демо-приглашение с лимитом и сроком, а после показа — отключение всех вошедших по нему одной кнопкой.
Желаемый результат
Админ в «👥 Участники» жмёт «🎤 Демо-QR» → QR на N входов (по умолчанию 50), живёт несколько часов (по умолчанию 3 ч); в подписи — лимит и срок.
По этому QR входят до N разных людей; N+1-й, или после срока, — тот же отказ, что сейчас.
Обычное «➕ Пригласить» по-прежнему одноразовое.
«🧹 Отключить гостей демо» (с подтверждением) отзывает доступ у всех, кто вошёл по демо-приглашению; админы не затрагиваются.
app/bot/routers/members.py, config/texts.yaml: кнопки «Демо-QR» и «Отключить гостей демо»
Тесты: лимит, гонка за последний вход, срок, отзыв гостей, миграция
SPEC A-3, ADR-011 (дополнение), docs/DEMO.md
Анализ
Затрагиваемые файлы
Файл
Что меняется
app/db/models.py, app/db/migrations/versions/
поля лимита и связь участник → приглашение
app/access/service.py
многоразовый захват, массовый отзыв
app/bot/routers/members.py, config/texts.yaml
UI админа
app/config.py, .env.example
лимит и срок демо-QR
docs/SPEC.md, docs/DECISIONS.md, docs/DEMO.md
исключение из одноразовости
Ограничения и риски
Гость = обычный участник: может коммитить (код идёт только в песочницу tgit-runner, guardrail 2), получает алерты, спрашивает агента (дневной лимит на человека уже есть).
QR на экране может сфотографировать кто угодно → короткий срок + лимит + массовый отзыв после показа.
Захват атомарный (UPDATE … WHERE uses < max_uses), как сейчас для одноразовых.
Как проверить
make lint && make test; на dev: админ создаёт демо-QR → 2–3 аккаунта входят → «Отключить гостей демо» → их кнопки перестают работать.
Лог выполнения
## Описание
На защите (`docs/DEMO.md`) хотим, чтобы любой из зала отсканировал **один QR** с экрана и попробовал бота. Сейчас приглашение одноразовое (SPEC A-3, ADR-011): первый, кто отсканирует, его «забирает», остальные получают отказ. Нужно многоразовое демо-приглашение с лимитом и сроком, а после показа — отключение всех вошедших по нему одной кнопкой.
## Желаемый результат
- Админ в «👥 Участники» жмёт «🎤 Демо-QR» → QR на N входов (по умолчанию 50), живёт несколько часов (по умолчанию 3 ч); в подписи — лимит и срок.
- По этому QR входят до N разных людей; N+1-й, или после срока, — тот же отказ, что сейчас.
- Обычное «➕ Пригласить» по-прежнему одноразовое.
- «🧹 Отключить гостей демо» (с подтверждением) отзывает доступ у всех, кто вошёл по демо-приглашению; админы не затрагиваются.
## Подзадачи
- [ ] `app/db/models.py`: `Invite.max_uses` (1), `Invite.uses` (0); `User.invite_id` → `invites.id` — миграция alembic, существующие использованные приглашения → `uses=1`
- [ ] `app/access/service.py`: `create_invite(max_uses, ttl)`, атомарный захват `uses < max_uses`, `revoke_demo_guests()`
- [ ] `app/config.py`: `DEMO_INVITE_MAX_USES`, `DEMO_INVITE_TTL_HOURS`
- [ ] `app/bot/routers/members.py`, `config/texts.yaml`: кнопки «Демо-QR» и «Отключить гостей демо»
- [ ] Тесты: лимит, гонка за последний вход, срок, отзыв гостей, миграция
- [ ] SPEC A-3, ADR-011 (дополнение), `docs/DEMO.md`
## Анализ
### Затрагиваемые файлы
| Файл | Что меняется |
|------|--------------|
| `app/db/models.py`, `app/db/migrations/versions/` | поля лимита и связь участник → приглашение |
| `app/access/service.py` | многоразовый захват, массовый отзыв |
| `app/bot/routers/members.py`, `config/texts.yaml` | UI админа |
| `app/config.py`, `.env.example` | лимит и срок демо-QR |
| `docs/SPEC.md`, `docs/DECISIONS.md`, `docs/DEMO.md` | исключение из одноразовости |
### Ограничения и риски
- Гость = обычный участник: может коммитить (код идёт только в песочницу `tgit-runner`, guardrail 2), получает алерты, спрашивает агента (дневной лимит на человека уже есть).
- QR на экране может сфотографировать кто угодно → короткий срок + лимит + массовый отзыв после показа.
- Захват атомарный (`UPDATE … WHERE uses < max_uses`), как сейчас для одноразовых.
## Как проверить
`make lint && make test`; на dev: админ создаёт демо-QR → 2–3 аккаунта входят → «Отключить гостей демо» → их кнопки перестают работать.
---
## Лог выполнения
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Описание
На защите (
docs/DEMO.md) хотим, чтобы любой из зала отсканировал один QR с экрана и попробовал бота. Сейчас приглашение одноразовое (SPEC A-3, ADR-011): первый, кто отсканирует, его «забирает», остальные получают отказ. Нужно многоразовое демо-приглашение с лимитом и сроком, а после показа — отключение всех вошедших по нему одной кнопкой.Желаемый результат
Подзадачи
app/db/models.py:Invite.max_uses(1),Invite.uses(0);User.invite_id→invites.id— миграция alembic, существующие использованные приглашения →uses=1app/access/service.py:create_invite(max_uses, ttl), атомарный захватuses < max_uses,revoke_demo_guests()app/config.py:DEMO_INVITE_MAX_USES,DEMO_INVITE_TTL_HOURSapp/bot/routers/members.py,config/texts.yaml: кнопки «Демо-QR» и «Отключить гостей демо»docs/DEMO.mdАнализ
Затрагиваемые файлы
app/db/models.py,app/db/migrations/versions/app/access/service.pyapp/bot/routers/members.py,config/texts.yamlapp/config.py,.env.exampledocs/SPEC.md,docs/DECISIONS.md,docs/DEMO.mdОграничения и риски
tgit-runner, guardrail 2), получает алерты, спрашивает агента (дневной лимит на человека уже есть).UPDATE … WHERE uses < max_uses), как сейчас для одноразовых.Как проверить
make lint && make test; на dev: админ создаёт демо-QR → 2–3 аккаунта входят → «Отключить гостей демо» → их кнопки перестают работать.Лог выполнения
2026-10-22
invites.max_uses/uses, флагusers.guestвместоusers.invite_id(иначе цикл FK users ↔ invites).revoke_guests()заодно гасит действующие демо-QR.usersи валил FK с данными → обычный ADD COLUMN. Проверено на Postgres 16.make lint && make test— 368 passed. PR #56.