Skip to main content
Glama
najikay

najamjad-cop

by najikay

Pursuit League — агент-полицейский 👮

Сторона полицейского в финальном проекте на двоих по курсу Orchestration of AI Agents: распределённая игра в «кошки-мышки» между двумя агентами, играемая peer-to-peer через MCP (FastMCP/HTTP) против агентов других команд, с целостностью commit-reveal на SHA-256 и отправкой результатов через Gmail API.

Репозиторий-компаньон (агент-вор): https://github.com/najikay/pursuit-thief-agent Общий базовый пакет байт-в-байт идентичен в обоих репозиториях, что обеспечивается scripts/sync_core.py в CI (см. docs/PLAN.md, ADR-002).

ci

Команда: Naji Kayal · Amjad Abed Статус: M6 — сыграно и подано шесть засчитанных серий против шести разных соперников, порог прохождения правила 31 превышен трижды. Играет полную проверяемую серию, взаимодействует с эталонным симулятором курса и с шестью независимыми реализациями команд, а после матча проверяет игру соперника. Оставшаяся работа отслеживается в docs/TODO.md.

Турнирная таблица

#

Дата

Соперник

Результат

Мы

Они

1

2026-08-08

uoh-ay26

победа 6–0

90

30

2

2026-08-13

imreeyal

победа 6–0

90

30

3

2026-08-14

vibecode

поражение 0–6

30

90

4

2026-08-17

MOAAMOHA

победа 4–2

60

40

5

2026-08-18

nis-yar1

поражение 0–6

30

90

6

2026-08-21

ahk-yosi

ничья 3–3

75

75

Тридцать шесть из тридцати шести мини-игр получили Verified OK при проверке, в шести засчитанных сериях, с нулём технических поражений по нашей вине. Именно на это число мы бы указали в первую очередь: каждая сыгранная нами игра была такой, что обе стороны могли пересчитать хэш и согласиться с результатом, включая две, которые мы крупно проиграли.

Две серии сверены поле-в-поле с собственным поданным отчётом соперника — у vibecode по 66 полям с нулём расхождений, а позже товарищеская игра против anrbj666 совпала по всем шести под-играм, по обоим значениям mutual_agreement.sha256 и по обеим парам github_commit для каждого окна. Отчёт, совпадающий с отчётом соперника, — единственный, который нельзя аннулировать по правилам 33–35, и для нас он ценнее, чем счёт на табло.


Содержание


Related MCP server: Police MCP Server

Установка

Требования

Python

3.12+ (управляется через uv — предустанавливать не нужно)

uv

единственный используемый здесь менеджер пакетов (правила §8.4)

ОС

Linux, macOS или Windows через WSL2 — разрабатывалось на WSL2

Необязательно

cloudflared для публичного туннеля; проект Google Cloud для email-отчётов

git clone https://github.com/najikay/pursuit-cop-agent.git
cd pursuit-cop-agent
uv sync                       # installs the locked dependency set
uv run python scripts/check_all.py   # every CI gate, one PASS/FAIL verdict

Прохождение check_all.py означает, что установка корректна: линт, ограничения размера файлов, правила репозитория, проверка типов и полный набор тестов.

Секреты — скопируйте .env-example в .env и заполните реальными значениями. Никакие секреты никогда не коммитятся; .gitignore покрывает .env, secrets/, token.json и credentials.json, а CI-гейт ломает сборку, если такой файл когда-либо попадает под отслеживание (правила книги 39–40).

cp .env-example .env          # then edit: ANTHROPIC_API_KEY, DEEPSEEK_API_KEY, …

Устранение неполадок

Симптом

Причина и решение

port 8802 … already in use

Запущен другой агент. Остановите его или измените network.my_port в config/police/game.toml.

peer отвечает ~15 с

Это нормально: импорт MCP-стека. Когда агент действительно готов, он печатает Uvicorn running — запускайте его задолго до матча.

Соперник сообщает, что мы недоступны

Проверьте туннель: uv run najamjad-cop preflight. 502 означает, что агент не запущен; ошибка JSON-RPC вида "Client must accept text/event-stream" означает, что агент здоров — это браузер обращается к MCP-эндпоинту.

Failed to spawn: najamjad-cop

Запускайте из корня репозитория; консольный скрипт находится в .venv этого репозитория.

Браузер OAuth не открывается (WSL)

Это ожидаемо — в WSL нет браузера по умолчанию. Используйте uv run python scripts/authorise_gmail.py --manual и вставьте URL самостоятельно.

Всё медленно на /mnt/c

Файловые системы Windows медленные в WSL. Журнал событий держит свой дескриптор открытым именно по этой причине; если возможно, держите рабочее пространство на файловой системе Linux.


Командная строка

Один консольный скрипт на репозиторий (najamjad-cop здесь, najamjad-thief в репозитории-компаньоне). Каждый глагол — это разбор аргументов плюс один вызов SDK — в CLI нет игровой логики, и мета-тест следит за тем, чтобы так и оставалось.

uv run najamjad-cop --help                     # every verb
uv run najamjad-cop version                    # code version (book rule 53)

uv run najamjad-cop preflight                  # match-day checklist
uv run najamjad-cop peer                       # serve: MCP server + tunnel + dashboard
uv run najamjad-cop match                      # serve, then play the agreed series
uv run najamjad-cop peer --no-tunnel --no-dashboard   # local play, nothing exposed

# Re-hash every step of a log and print the verdict. Paths are literal -
# `<log>` would be read by the shell as a redirect, so use a real one:
uv run najamjad-cop replay tests/goldens/artifacts/log_segal-police-team-vs-segal-thief-team_g01.json
uv run najamjad-cop replay path/to/log.json --serve   # open the viewer instead
uv run najamjad-cop archive match.zip                 # bundle evidence (secrets excluded)

Коды выхода, потому что они используются в скриптах:

Код

Значение

Пример

0

сработало

preflight готов, журнал проверен

1

выполнилось, ответ плохой

не готов к матчу, журнал ИЗМЕНЁН

2

не смогло выполниться

файл журнала отсутствует или нечитаем

Изменённый журнал и отсутствующий файл — намеренно разные коды: результат проверки нельзя спутать с опечаткой.

Команды для разработки

uv run python scripts/check_all.py                  # all CI gates, one verdict
uv run python scripts/self_play.py --games 100      # measure our brains vs baselines
uv run python scripts/demo_dashboard.py             # dashboard over a played game
uv run python scripts/two_process_match.py          # both repos as real processes
uv run python scripts/sync_core.py ../pursuit-thief-agent   # verify the mirrored core

Запуск

Всё ниже работает из свежего клона без соперника и без ключей API. Агент играет полную проверяемую серию на одних шаблонах (книга, стр. 67) — LLM является улучшением, а не зависимостью.

1. Установите и подтвердите установку

git clone https://github.com/najikay/pursuit-cop-agent.git
cd pursuit-cop-agent
uv sync                                     # locked dependency set
uv run python scripts/check_all.py          # every CI gate, one verdict

ALL GATES PASSED означает, что линт, размеры файлов, правила репозитория, типы, набор стратегий и более 1 900 тестов зелёные.

2. Запустите peer-сервер с панелью управления

uv run najamjad-cop peer --dashboard --no-tunnel

Дождитесь Uvicorn running — именно это, а не первая строка журнала, означает готовность. Холодный старт занимает ~15 с (импорт MCP-стека), так что в день матча запускайтесь заранее.

Затем откройте http://127.0.0.1:8000/.

Порт и хост берутся из config/setup.json (ui.port, ui.host). Панель привязана к loopback намеренно: она показывает наши представления и наши запечатанные состояния, поэтому её публикация отдала бы сопернику всё то, что commit-reveal существует, чтобы скрывать (правила 8–9).

Что вы получаете:

Панель

Показывает

Board

Тепловая карта представлений в логарифмическом масштабе — линейная сжала 47 из 48 ячеек в одну полосу

Turn banner

Чей ход, какой шаг, какая фаза

Dialogue

Все подсказки входящие и исходящие, с моделью, которая их написала

Negotiation

Предложение → контрпредложение → фиксация, и условия, ожидающие человека

Budget

Токены относительно согласованного лимита в 200k

Match day

Готовность — те же проверки, что выполняет preflight

Testing

Режим практики и отвечает ли каждый эндпоинт

Matches

Каждый поданный нами матч: счёт, вердикты проверки по играм, артефакты

Events

Сырой поток событий

Обновления приходят через WebSocket; клиент никогда не опрашивает. Сбой панели не может повлиять на игру — она лишь подписчик и ничего больше (ADR-005).

Дополнительные элементы управления. Установите features.controls в true в config/setup.json, чтобы включить запуск/остановку, одобрение переговоров и переключатель режима практики со страницы. По умолчанию выключено, и намеренно нет кнопки, которая играет засчитанный матч — он оценивается и необратим, и docs/RUNBOOK.md является интерфейсом для него.

2b. Тестирование без обращения к преподавателю

Панель Testing отвечает на два вопроса, которые иначе означают копание в журналах.

В каком я режиме? Режим практики перенаправляет каждый отчёт в ваш собственный почтовый ящик вместо ящика преподавателя и добавляет префикс [PRACTICE] к теме. Он не пропускает отправку — отправка была тем шагом, на котором проигрывались матчи в задании 6, поэтому практический прогон выполняет её по-настоящему, и вы читаете реальное письмо. Перенаправление обеспечивается дважды: адрес переписывается, а затем проверяется в точке невозврата, так что молчаливо неудачная перезапись вызывает исключение вместо доставки. См. docs/CONFIG.md §3b.

Проще всего флаг — он включает практику для одного процесса и ничего не трогает:

uv run najamjad-cop match --opponent amjad --practice --dashboard --tunnel

Или переключите его с панели (при включённом features.controls), или установите practice.enabled в config/setup.json, чтобы сделать его постоянным. Он читается заново при каждой сборке отчёта, так что переключатель действует без перезапуска, и он переопределяет email.mode на send — практический прогон, который тихо создал черновик, выглядел бы точно так же, как успешная отправка.

Кто-нибудь вообще доступен? Probe endpoints обращается к нашему MCP-URL и URL соперника и сообщает о трёх состояниях, а не двух:

Состояние

Значение

зелёный

что-то приняло TCP-соединение там

красный

настроено, но никто не слушает — это блокирует матч

серый

ещё не настроено — матч не запланирован, это не ошибка

Зелёный свет означает, что порт ответил. Это не означает, что протокол работает или что соперник согласится на наши условия — для этого существует рукопожатие, и панель намеренно не заявляет больше того, что может проверить.

3. Проверьте, что вы готовы играть

uv run najamjad-cop preflight        # exit 0 or do not play
uv run python scripts/pre_match_smoke.py     # MATCH READY in ~50 s

4. Играйте

Опишите карточку соперника один раз, затем называйте её по имени. opponents/<name>.toml несёт два факта, которые приходят от них — их MCP-эндпоинт и group_id, объявляемый их рукопожатием:

url      = "https://their-agent.example.com/mcp"
group_id = "their-group"
name     = "Their Team"
notes    = "quick tunnel - URL changes if cloudflared restarts"

Скопируйте opponents/_template.toml для начала. Затем каждый глагол принимает --opponent:

uv run najamjad-cop preflight --opponent amjad
uv run najamjad-cop match --opponent amjad --dashboard --no-tunnel

Ничего отслеживаемого редактировать не нужно, настройки последнего соперника не перезаписываются следующим, а opponent_group_id — который должен совпадать с тем, что объявляет их рукопожатие, иначе отчёт привязывается к заглушке вместо их имени — можно проверить за день до матча, а не обнаружить в момент рукопожатия.

Карточка может задавать только network.opponent_*. Игровые термины согласованы в подписанном config/game.json, и переопределение одного из них для конкретного противника — это именно то, что никогда не должно быть простым.

Или задайте network.opponent_url в config/police/game.toml вручную, затем:

uv run najamjad-cop match --dashboard --no-tunnel

Завершённый матч записывает четыре артефакта согласно Приложению F в workspace/artifacts/ — декларацию, конфиг, журнал и результат — и отправляет результат по почте. Проверьте их с помощью:

uv run python scripts/post_match.py --opponent <name>

5. Попробуйте без противника

Два способа, оба настоящие:

# our cop against our thief, two OS processes over real MCP/HTTP
uv run python scripts/two_process_match.py

# against the course reference simulator (expects ../reference-sim)
uv run python scripts/rehearsal.py --games 6

Второй — тот, что имеет значение: это единственная конфигурация, которая когда-либо выявляла наши дефекты интероперабельности, потому что это единственный противник, которого мы не писали.

6. Проверка журнала

uv run najamjad-cop replay workspace/artifacts/log_<game_id>_g01.json

Код выхода 0Verified OK; код 1TAMPERED и указывает на сбойный шаг; код 2 означает, что файл не удалось прочитать. Подделанный журнал и опечатка — намеренно разные коды: вердикт аудита никогда не должен быть спутан с ошибочно набранным путём.

Добавьте --serve, чтобы открыть просмотрщик вместо вывода вердикта.

7. Измерение

uv run python scripts/strategy_smoke.py --games 50   # win rates with intervals
uv run python scripts/sweep.py --games 24            # parameter sensitivity
uv run python scripts/measure_tokens.py              # token census

Результаты попадают в results/, и именно их отображает notebooks/analysis.ipynb.

Рабочий процесс дня матча

Полная процедура с точными командами — в docs/RUNBOOK.md. Вкратце:

  1. Разогрев — запустите агента заранее; холодный старт — ~15 с.

  2. Предварительная проверкаuv run najamjad-cop preflight; код выхода 0, иначе не играть.

  3. Обмен URL — задайте network.opponent_url в config/police/game.toml.

  4. Играuv run najamjad-cop match, панель на http://127.0.0.1:8000/.

  5. Аудит — автоматически для каждой мини-игры; каждая игра должна показать Verified OK.

  6. Отчёт — сверьтесь с противником, затем отправьте (правило 30: только gmail.send).

  7. Архивuv run najamjad-cop archive match.zip (секреты исключены).


Конфигурация

Файл

Роль

config/game.json

Общие подписанные условия. Оба участника должны иметь побайтово идентичную копию; рукопожатие отказывается играть при любом расхождении. Наш — это стартовое предложение: каждое значение на уровне или выше минимума Приложения F (правило 12: повышать, никогда не понижать).

config/police/game.toml

Приватный, локальный. Наш порт, URL противника, имя туннельного хоста, выбор LLM, настройка убеждений. Никогда не покидает сеть.

config/rate_limits.json

Настройки ограничителя для каждого сервиса, проверяемые на соответствие потолкам Приложения F при загрузке.

.env

Только секреты. Никогда не коммитится.

Параметры, о которых стоит знать:

Ключ

Эффект

network.my_port

Наш порт MCP (8802 коп / 8801 вор, чтобы оба работали локально).

network.opponent_url

Единственное, что мы знаем о противнике. Предварительная проверка завершается ошибкой, пока пусто.

tunnel.hostname

Постоянное публичное имя. Именованный туннель сохраняет свой URL между перезапусками — дефект, стоивший Заданию 6 больше всего времени (ADR-004).

llm.every_n_steps

Частота подсказок. Ручка качества, а не экономии — см. docs/TOKEN_BUDGET.md.

belief.smell_trust_weight

Насколько мы доверяем запаху в сравнении с возможно лживой подсказкой.

movement_and_barriers.*

Согласованные правила. Их одностороннее изменение ломает подпись.


Панель управления

Запустите её с --dashboard и откройте http://127.0.0.1:8000/ — см. Запуск §2 о панелях и дополнительных элементах управления.

Тепловая карта убеждений, баннер хода, диалог с указанием модели для каждого сообщения, шкала времени переговоров, бюджет токенов и статус доставки отчёта — всё передаётся через WebSocket, никогда не опрашивается. Она показывает только локальную истину (правила книги 8–9): позиция противника не имеет поля в модели чтения, и мета-тест гарантирует, что UI может обращаться к агенту только через SDK.

Живая панель

Просмотрщик повторов

Каждый шаг заново хэшируется из раскрытых (payload, nonce) и сравнивается с сохранённым обязательством (правило книги 20). Ниже — собственный образец журнала лектора, воспроизведённый чисто:

Verified OK

И тот же журнал с одной записью, отредактированной задним числом, — подделка локализуется ровно на том шаге, куда она была внедрена, и правило 19 аннулирует игру:

Tampered

См. assets/README.md о том, как воспроизводится каждое изображение.


Академический отчёт

Модель: Dec-POMDP, который не видит ни одна сторона

Игра — это децентрализованный частично наблюдаемый марковский процесс принятия решений. Ни один агент не наблюдает истинное состояние: позиции запечатаны в обязательствах до финального аудита, поэтому каждый участник держит убеждение о том, где может быть другой, и действует на его основе.

Два канала наблюдения с противоположными свойствами доверия:

  • Запах — затухающий феромонный след, который противник испускает непроизвольно и не может подделать (книга, СТР. 22). Неподделываемый, но размытый.

    Поставляются две модели, и на каждый матч можно выбрать любую. Книжная (СТР. 43–44) — радиальная — 0.90 / 0.62 / 0.42 / 0.20 / 0.14 / 0.04 — с относительным затуханием τ ← (1-ρ)·τ; эталонный симулятор — линейный по расстоянию Чебышёва — кольца 0.90 / 0.60 / 0.30 — с абсолютным затуханием τ ← τ - ρ. Мы реализуем обе, и обе зарегистрированы в интероп-наборе: ScentModel.BOOK — это multiplicative_book_v1 (934c220d…), а ScentModel.REFERENCE — это subtractive_chebyshev_v1 (81ebee59…). Каждая воспроизводит собственные опубликованные векторы набора — включая порядок отправки по каналу, который field_walk набора фиксирует как состарить предыдущий след, слить свежее отложение без затухания, передать это: наш вор отправлял след на один шаг затухания слишком свежим до 2026-08-22, покадровый шлюз противника это измерил, и кадры теперь закреплены по ячейкам против обхода (test_wire_scent_serve_order.py). Так что подбор под противника — это один ключ — pheromones.pheromone_model в config/game.json — и не изменение четырнадцати подписанных условий, поэтому дайджест контракта a284082d… переживает переключение, и никому не нужно переподписывать. Дайджест, который мы объявляем при переговорах, берётся из сконфигурированной модели, так что не существует состояния, в котором мы передаём одну физику, а заявляем другую. Эмиссия отдельно регулируется от подсказок — --scent full|window|none и --hints/--no-hints — так что полностью тихая серия — это один флаг; в режиме тишины мы не объявляем никакой модели вообще, потому что заявление о поле, которое никто не отправляет, — это не заявление, которое стоит делать.

  • Подсказки — свободный естественный язык, которому правила явно разрешают быть ложью (правила 26–27). Точные, но ненадёжные.

Наш механизм убеждений объединяет их: диффузия для движения, обновление правдоподобия запаха и вес достоверности для каждого противника, который растёт и падает по мере того, как их подсказки согласуются или расходятся со следом. Полный вывод — в docs/PRD_belief_engine.md.

Три вывода из его построения, каждый из которых изменил дизайн:

  • Затухание запаха имело неподвижную точку. Относительное затухание, округлённое до трёх знаков, никогда не достигало нуля, поэтому мёртвые следы вечно загрязняли убеждение. Исправлено явным эпсилоном.

  • Мультипликативное слияние дважды учитывало кумулятивное поле запаха и оставляло убеждение отстающим от движущегося противника на ~5 ячеек. Заменено на устойчивое обновление смеси.

  • Плоское правдоподобие парковало убеждение в середине следа. Заострение плюс аддитивный минимум исправили это; ограничение делало слабый запах неотличимым от его отсутствия.

Дилеммы оркестрации

Один шлюз или много вызывающих? Правило книги 3 предписывает оркестратор, и мы поняли его буквально: периферийные модули никогда не вызывают друг друга. Механизм убеждений ничего не знает о транспорте, стратегия ничего не знает о криптографии, транспорт ничего не знает о правилах. Именно это делает весь цикл хода тестируемым против заглушек — шов, которого у Задания 6 никогда не было.

Насколько доверять заглушке. Наш самый острый урок. Три отдельных дефекта пережили 1500 прошедших тестов, потому что заглушки были добрее, чем канал: фиктивный транспорт, оборачивавший аудиторскую полезную нагрузку так, как настоящий не оборачивал, внутрипроцессная ссылка, которая никогда не блокировалась, и участники, которые играли только сами с собой. Интероперабельность можно проверить только против того, что вы не писали.

Куда поместить дедлайн. Участник, отвечающий вечно, не должен удерживать нас в решённой игре, а участник, замолчавший, не должен стать нашей технической потерей. Каждое ожидание ограничено, и каждое завершение объявляется, а не предполагается — см. ниже.

Ограничение скорости нашего собственного протокола. Привратник существует, чтобы быть хорошим гражданином по отношению к Anthropic и Gmail. Применение его к противнику едва не стоило нам игр: наш собственный ограничитель мог задержать ответ за их 30-секундный дедлайн.

Стратегии и почему не RL

Коп — скриптованная печать-половинка (strategy/seal_cop.py), а не покадровая оценка: запечатать среднюю колонку, идя по соседней полосе, взять ворота, отрезать половину, где находится вор, и вор оказывается в кармане 3×3 с нами. Оттуда domain/endgame.py решает карман точно — размещение барьера — это ход в его дереве игры — и оценивает каждую победу в совместное расположение, потому что заявление о наступлении — это единственный захват, который чтит каждая реализация; вор, запечатанный там, куда мы никогда не можем дойти, оценивается как remote seal нашим собственным стендом и отклоняется как план. Блокировка одним барьером (strategy/lock.py: наше тело плюс одна стена, требуется смежность) завершает прижатого вора рано. Стенд (tests/regression/) гоняет её против реагирующих противников — включая вора, который сидит на собственной линии стены скрипта, — и она превращает всех них в пределах 35 ходов, обычным заявляемым захватом (test_seal_converts_every_reacting_thief.py). Преследование, управляемое убеждением (cop_brain.py), остаётся запасным вариантом для доски без локализации.

Вор — уклонение с горизонтом выживания как стопка жёстких минимумов, а не жадная максимизация дистанции: никогда не заканчивать ход в пределах одного шага от копа, отказываться от ячеек, которые полупостроенный забор уже сделал дешёвыми для запечатывания, держать достижимый квадрат 4×4, оставаться на стороне копа от формирующегося разреза (запечатывающий не может завершить стену, отделяющую его от нас), и стоять в зазорах строящегося забора — предпочтение, которое выполняется после минимумов безопасности, потому что до них оно однажды завело нас в угол, который противник затем запечатал (test_ahk_yosi_corner_hunt.py воспроизводит эту линию убийства ход за ходом). Оно переживает каждую архивную линию противника в matches/ и все 24 варианта тай-брейка реагирующего копа-охотника за углами.

Политика подсказок — блеф — это стратегический ресурс с ценой: заявление раскрывает ячейку заявляющего, поэтому ложное заявление о захвате отдаёт вору нашу точную позицию задаром.

Никакого обучения с подкреплением, намеренно:

  • По всей лиге доступно всего ~60 реальных игр — этого далеко не достаточно, чтобы обучить политику на пространстве состояний такого размера.

  • Поведение противников нестационарно; каждая команда использует что-то своё.

  • Недетерминизм сломал бы воспроизводимость, а воспроизводимость — оцениваемый результат.

  • Эвристики уже решительно превосходят базовый уровень (ниже), так что RL — это риск без измеренной выгоды.

Вместо этого мы занимаемся онлайн-моделированием противника, рассчитанным на ~210 наблюдений, которые реально даёт серия, — достоверность подсказок, тенденции ходов, реакция на барьеры.

Измерено с помощью реального механизма матчей на отложенных играх — сид 11, никогда не использовавшийся при настройке, 60 игр на каждое противостояние:

противостояние

захваты

доля

95 % ДИ Уилсона

наш коп против жадного вора

60/60

100 %

94–100 %

жадный коп против нашего вора

0/60

0 % (100 % выживание)

0–6 %

жадный против жадного (эталон)

4/60

6.7 %

2.6–16 %

Ноль разногласий между пирами и ноль сбоев аудита во всех 180 играх.

наша стратегия против жадного базового уровня

Один настраиваемый параметр решает исход матча, и это не тот, который мы ожидали:

какой параметр решает игру

barrier_threshold меняет долю захватов с 4 % до 100 % во всём диапазоне. Барьер непроходим для обеих сторон, поэтому коп, который ставит стену на слабых основаниях, отгораживается от вора, которого преследует, — мы две недели поставляли 0.15, что стоило нам примерно трети игр. lookahead — настоящий нулевой результат: глубины 1–4 дают побайтово идентичные игры, потому что изотропное диффузионное ядро сохраняет ранжирование кандидатных ходов, которое оно должно разделять.

Оговорка, которую мы обязаны сделать. Наш вор выживает против каждого копа, с которым мы когда-либо сталкивались или которого архивировали, — и всё же проигрывает, примерно на шаге 30, нашему собственному запечатывающему копу, чей план половинного деления не показал ни один противник. Оба направления зафиксированы, а не сглажены (test_thief_beats_sealing_cops.py фиксирует поражение и его цену; наборы corner-hunt и archive фиксируют выживания), потому что вор, оценённый только против копов, которых он побеждает, оценён против самого себя.

Полные выводы, доверительные интервалы, таблица стоимости токенов и ссылки находятся в notebooks/analysis.ipynb. Воспроизвести с помощью:

uv run python scripts/baselines.py --games 60 --seed 11   # held-out comparison
uv run python scripts/sweep.py --games 24 --seed 7        # sensitivity sweeps
uv run python scripts/measure_tokens.py                   # token census

Что нам дало тестирование против незнакомца

Мы клонировали эталонный симулятор курса и направили его на себя. Ничего не работало ни в одну, ни в другую сторону. Эталон называет аргумент инструмента MCP message для трёх инструментов и payload для одного; мы отправляли payload во все четыре и принимали только payload. Каждый ход и каждое предложение отклонялись при связывании аргументов до того, как выполнялся хотя бы один байт игровой логики — против любого агента, построенного на эталоне, а таких большинство в классе.

За этим последовали ещё четыре несовместимости: обязательный timestamp, который мы никогда не отправляли, поле claimed_cell, которое их парсер отвергает наотрез, и три поля claim, типы которых различались. Затем выяснилось, что аудит-раскрытие отправляется как простой список, тогда как схема объявляет конверт, поэтому оба пира записали TAMPERED для игр, в которых никто не жульничал.

Каждая из этих проблем проходила наши собственные тесты. Ядро commit-reveal, напротив, пережило контакт без изменений: наш commit_of воспроизводит сигнатуру эталона байт в байт.

Урок, который мы вынесли для любого распределённого проекта: зелёный набор тестов доказывает, что ваш код согласуется с вашими предположениями, а не то, что ваши предположения верны.


Документация

Документ

Назначение

docs/PRD.md

Требования к продукту (идентификаторы FR-*, KPI, вехи)

docs/PLAN.md

Архитектура: диаграммы C4 + FSM, ADR-001..021, карта модулей

docs/TODO.md

План сборки из 688 задач с трассируемостью и прогрессом

docs/HANDOFF-2026-08-14.md

Текущее состояние, открытые вопросы и каждое измеренное исправление

docs/PROTOCOL.md

Что пересекает провод, а что никогда

docs/SECURITY.md

Модель угроз, защита от prompt-инъекций, обработка секретов

docs/UX.md

Эвристики Нильсена, сопоставленные с решениями дашборда; доступность

docs/EXTENDING.md

Четыре точки расширения с рабочим плагином

docs/CONFIG.md

Каждый ключ конфигурации, его файл и его согласуемость по Приложению F

docs/CI.md

Что проверяет каждый шлюз и как воспроизвести сбой

CONTRIBUTING.md

Соглашения: синхронизация ядра, коммиты, тесты, заморозка в день матча

docs/ISO25010.md

Характеристики качества ISO/IEC 25010, сопоставленные с доказательствами

docs/edge-cases.md

Каждое обработанное граничное условие, каждое со ссылкой на свой тест

docs/TOKEN_BUDGET.md

Измеренное потребление токенов и модель затрат

docs/OPEN_ITEMS.md

Что известно как неполное, с доказательствами

notebooks/analysis.ipynb

Исследования чувствительности, базовые уровни, таблица затрат, ссылки

docs/PRD_belief_engine.md · PRD_commit_reveal.md · PRD_llm_router.md

Проектирование механизмов

docs/runbook-network.md

Процедуры туннеля и подключения

docs/research/

Дайджесты источников (книга, руководства, эталонный симулятор, ретроспектива A6)


Аудит противника

Commit-reveal доказывает, что пир не переписывал историю. Он ничего не доказывает о том, играли ли они по правилам, и это разные гарантии — мы смешивали их на протяжении всего этапа лиги и не могли сказать после проигранной серии, была ли игра легальной.

Аудит по замыслу проводится после матча: правила 33–35 аннулируют матч при противоречивых отчётах, поэтому агент, действующий на основании собственного обвинения, превращает подозрение во взаимный ноль. Всё ниже фиксирует доказательства и ничего не меняет в том, как мы играем (PLAN ADR-019).

# replay their revealed records through the fair-play rules: movement legality,
# the Barrier Law, the budget, step order, hint length - and say what it could NOT check
uv run python scripts/audit_opponent.py --team vibecode

# are we disclosing scent on the same terms they are?
uv run python scripts/scent_parity.py --since 2026-08-14T16:00   # UTC

# 323 of 323 sealed capture claims name the claimer's own revealed cell
uv run python scripts/claim_evidence.py

# both repos must declare the same counted-match count (rules 37-38)
uv run python scripts/reconcile_counted.py ../pursuit-thief-agent --apply

Что может установить раскрытие и что может только провод: запечатанная запись пира содержит то, что этот пир решил запечатать. Движение и барьеры проверяемы по одному архиву; smell_grid, capture_claim, hint и время ответа проверяемы только по тому, что пришло по проводу, поэтому FrameLog хранит их так, как они были отправлены. Аудит сообщает о них как о непроверяемых, а не включает их в чистый вердикт — «мы посмотрели и согласились» и «смотреть было не на что» никогда не должны читаться одинаково.

Три результата, которые ограничивают любую стратегию

Все они были установлены измерениями во время этапа лиги, и все они имеют решающее значение.

Барьер может уменьшить доску, но никогда не может взять вора. В книге даны три условия захвата (правила 46–47); эталон курса реализует ровно одно. В его rules.py есть thief_result и is_captured, и нигде нет проверки захвата барьером или обездвиживания. Каждый противник, которого мы встречали, происходит из эталона, поэтому окружение даёт мини-игру, которую мы засчитываем, а они — нет — противоречие правил 33–35. Каждый захват должен быть заявлением, которое вор подтверждает (PLAN ADR-020).

Один коп не может сойтись на открытой доске. Сетка 7×7 — это декартово произведение двух путей, поэтому её число копов равно 2 (Maamoun & Meyniel, 1987), и исчерпывающая неподвижная точка по всем состояниям 49×49 не находит ни одного состояния, из которого коп, использующий только движение, может вынудить захват при одновременных ходах. Наш коп, преследующий вора до дистанции 2 и удерживающий её 28 шагов, — это теорема, а не дефект. Барьеры — единственный ресурс, который меняет ответ (PLAN ADR-021).

А с планом барьеры действительно меняют это. Запечатывание половинным делением превращает любого реагирующего вора, которого может построить стенд, — доска делится пополам, половина делится пополам, 3×3 решается точно, заканчиваясь заявлением о совместном нахождении в пределах 35 ходов. Два указанных выше ограничения по-прежнему управляют формой этой победы: она должна заканчиваться заявлением, которое вор подтверждает, и её нельзя получить только движением. То, что долго фиксировалось здесь как наш самый большой конкурентный риск — коп, который преследует до дистанции 2 и удерживает, — закрыто; остающийся риск — это противник, чей собственный коп играет по плану, столь же полному, как наш, и именно против этого оцениваются нижние границы вора.

tests/regression/cop_duel.py — это эталон со стороны копа, с помощью которого проверяются эти утверждения: наш коп против адаптивного вора, с убеждением, которое реальный путь входа строит по запаху. Записанная линия противника не реагирует и не может измерить сближение.


Лицензия и атрибуция

MIT (см. LICENSE). Формы протокола и схемы артефактов совместимы с эталонным симулятором курса rmisegal/Game-P2P-Cop-Chase (образовательная лицензия); в случае конфликта между книгой и кодом главенствует книга.

Создано с помощью FastMCP, FastAPI, pydantic и uv.

Maintenance

ActivityActive
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    B
    maintenance
    Implements a distributed cops-and-robbers game agent as a FastMCP server, enabling peer-to-peer play with no central server. It manages turn-based moves, belief tracking, strategy selection, and secure protocol via SHA-256 commit-reveal.
  • F
    license
    Not graded
    quality
    B
    maintenance
    Runs a decentralized thief agent for a peer-to-peer cops-and-robbers game, using FastMCP to exchange moves and messages with a police agent while employing Bayesian belief and credibility-based bluffing strategies.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/najikay/pursuit-cop-agent'

If you have feedback or need assistance with the MCP directory API, please join our Discord server