ADR-0009 · Бот-API и турниры

Статус: предложено · 2026-06-26 (дизайн; 3a должен быть bot-aware, сам API — в 3b)

Контекст. 4–5 внешних команд пишут своих Dice-Chess-ботов; Жегорс обсуждал с владельцами DiceChess.com кросс-командные бот-турниры. Нужен интерфейс для подключения чужих ботов + авторизация (токены). Требование пересекает сетевой протокол + аккаунты + auth → продумать до финализации фазы 3 и детализации 3a.

Решение. Внутри — одна комната, два адаптера: GameRoom транспортно-нейтрален, игрок = PlayerConnection (получает GameEvent, шлёт GameCommand, опознан Principal, привязан к Seat). Наружу для команд — выделенный Bot-API в форме Lichess: READ = долгоживущий ndjson-стрим (/stream/event + /bot/game/stream/{id}, первая строка = снапшот), WRITE = stateless идемпотентные POST (/move/{uci}, /turn батч, /cube/*, /resign, …). Dice-специфика: событие roll несёт кости + FEN + commit/reveal; легальность бот считает общим движком (opt-in ?legal=true), сервер валидирует при сабмите. Идентичность — одна Principal-система, три вида: Guest/User/Bot(team,name); бот = bot:team:<team>:<name>, player_type='bot', durable opaque-токен (single-author admin gate, ручная выдача для 4–5 команд), isBot из типа токена (anti-masquerade на уровне типов). source='playtourney' для турнирных партий. Турниры: двойной круговой с зеркальными костями (тот же seed зеркальной паре → удача гасится), Glicko-2 отдельный бот-ладдер, кросс-орг = single-host (aurora), bots-dial-in.

Последствия.

  • 3a обязан быть bot-aware (дёшево сейчас, дорого ретрофитить): транспортно-нейтральные GameCommand/GameEvent ADT (НЕ WS-фреймы; приёмочный тест — гонять комнату двумя in-memory connection без HTTP); PlayerConnection/Principal(вкл. Bot)/Seat(вкл. Spectator); PrincipalResolver интерфейс; per-principal admission-хук; DiceSource+commit-reveal с позиц.-независимым roll-stream; ingest-writer выводит identity из Principal. Сам бот-эндпоинт/токены/турнир-оркестратор — в 3b+.
  • Namespace bot:team:<team>:<name> — строгое надмножество bot:<algorithm> (ADR-0003): существующие id не меняются, коллизии команд невозможны; защищённый от V6-дедупе namespace. Гоча: external_id VARCHAR(50) — проверить влезаемость.
  • Боты — ось нагрузки (играют 24/7 back-to-back, нагенерят больше партий, чем люди): concurrency-caps, global admission с приоритетом людям, throttled writer→analytics, изоляция от analytics+Immich на aurora; полные турнирные логи в raw-архив на dexus, не в курируемый Postgres.
  • bot-vs-bot за забором от публичного корпуса (движок слабый/жадный, не эталон — эталон = win-rate сильнейших людей): флаг corpus/is_reference, дефолт non-reference для playtourney — headline-статистика остаётся на сильнейших людях.
  • Кросс-орг паритет движка правил критичен (нет рокировки Фишера; en-passant/terminal-color легаси-гочи) — пинить версию на событие.

Альтернативы (отклонены). Заставить ботов говорить на человеческом WS — враждебно к не-браузерным авторам (фрейминг/keepalive/ресинк), теряет данные на reconnect, не дебажится curl-ом. Две раздельные account-системы (люди/боты) — лишнее, один Principal с тремя видами. Дублировать движок для ботов — фатально (replay-валидация отвергнет по расхождениям). Федерация двух авторитетных серверов для кросс-орг — двойная авторитетность костей; single-host проще.

Детали: 04 Фаза 3 §5–6.

🔗 ADR-0007 Серверный авторитет и realtime (фаза 3) · ADR-0008 Честность кубиков — server CSPRNG + commit-reveal · 00 Журнал решений (ADR)