Skip to main content
Glama
najikay

najamjad-cop

by najikay

Pursuit League - Cop Agent 👮

El lado policial/agente de un proyecto final en pareja para Orchestration of AI Agents: un juego distribuido de policías y ladrones jugado de igual a igual sobre MCP (FastMCP/HTTP) contra agentes de otros equipos, con integridad de compromiso-revelación SHA-256 y envío de resultados mediante la API de Gmail.

Repositorio complementario (agente ladrón): https://github.com/najikay/pursuit-thief-agent El paquete central compartido es idéntico byte a byte en ambos repositorios, garantizado por scripts/sync_core.py en CI (ver docs/PLAN.md, ADR-002).

ci

Equipo: Naji Kayal · Amjad Abed Estado: M6 - seis series contabilizadas jugadas y registradas contra seis oponentes distintos, el umbral de aprobación de la regla 31 se ha superado en tres ocasiones. Juega una serie completa auditada, interopera con el simulador de referencia del curso y con seis implementaciones independientes de otros equipos, y audita la partida de un oponente después del encuentro. El trabajo restante se controla en docs/TODO.md.

Historial de la liga

#

Fecha

Oponente

Resultado

Nosotros

Ellos

1

2026-08-08

uoh-ay26

ganada 6–0

90

30

2

2026-08-13

imreeyal

ganada 6–0

90

30

3

2026-08-14

vibecode

perdida 0–6

30

90

4

2026-08-17

MOAAMOHA

ganada 4–2

60

40

5

2026-08-18

nis-yar1

perdida 0–6

30

90

6

2026-08-21

ahk-yosi

empatada 3–3

75

75

Treinta y seís de treinta y seís minijuegos verifcados como Verifed OK en la auditóría, en seis series contabillizadas, con cero pér didas técnicas atribuibles a nosotrús. Ese es el núméro que señalaríamos príme ro: cada juegó que jó gamús fue uno que ambós ladós podían recalculár y aceptár, incluidas las dos que perdímús estrepitósaménte.

Dós series se reconciliáron cambó por cambó con el infórme presentádó por el própio opónénte: la de vibécóde en 66 cambós con céró diferéncías, y un póstériór amistósó cóntra anrbj666 que cóincidía en lós seís subjuegós, en ambós valóres mutual_agreement.sha256 y en ambós pares de github_commit pór véntana. Un infórme que cóincide cón el del opónénte es el único tipó que nó puede invalidadarse según las reglas 33-35, y valé más para nótús qué un marcadór.


Contenidó


Related MCP server: Police MCP Server

Instalación

Requisitós

Pythón

3.12+ (géstiónádó pór uv: nó necesitás tenérló preinstaladó)

uv

el único gestór de paquetés usadó aquí (dírectrices §8.4)

SO

Linux, macÓS ó Windóws medianté WSL2: desarrólladó en WSL2

Opciónal

clóudflared para un túnel públicó; un próyectó de Góógle Clóud para el córreó de infórmes

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

Qué check_all.py pase significa qué la instalación es sósida: lint, límites de tamañó de archivó, reglas del repósitórió, cómprobación de tipós y la suité de pruebas cómpleta.

Secretós - cópia .env-example a .env y relléna lós valóres reales. Nunca se hace cómmit de nada secretó; .gitignóre cubre .env, secrets/, tóken.j sóne y credentials.j sóne, y una cómpuerta de CI hace fallar la cómpilación si algónó llega a rastrarse (reglas 39-40 del libró).

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

Sólución de próblemas

Síntóma

Causa y sólución

pórt 8802 … already in use

Ótró agénte se está ejecutándó. Detéñló ó cambia netwórk.my_pórt en cónfig/pólice/gamé.tóml.

peer tarda ~15 s en respóndér

Nórmal: impórtar la pila MCP. Imprime Uvicórn running cuándó está realménte listó: empiéza cóm tiémpó antes de un partidó.

El opónénte nós infórma cómó inalcanzables

Cómpruéba el túnel: uv run najamjad-cóp preflight. Un 502 significa qué el agénte nó se está ejecutándó; un errór JSON-RPC cómó "Client must accept ext/event-stream" significa qué está sanó: esó es un navegadór accedién dó a un éndpóint MCP.

Failed to spawn: najamjad-cóp

Ejecuta desde la raíz del repósitórió; el script de cónsóla vive en el .venv de esté repósitórió.

El navegadór ÓAuth nó se abré (WSL)

Es espéradó: WSL nó tiene navegadór predetérminadó. Usa uv run pythón scripts/authórise_gmail.py --manual y pega la URL tú mismó.

Tódó va lentó en /mnt/c

Lós sistémas de archivós móntadós desde Windóws són lentós en WSL. El registró de evéntós mantiéné su identificadór abiertó exactaménte pór esta razón; si puédes, mantén el espació de trabajó en el sistéma de archivós Linux.


Línea de cómandós

Un script de cónsóla pór repósitórió (najamjad-cóp aquí, najamjad-thief en el cómpleméntarió). Cada verbó es anáisis de arguméntós más una única llamada al SDK: la CLI nó cóntiéné lógica de juegó, y una meta-prueba ló garantiza.

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)

Códigós de salida, pórqué se ejecutan en scripts:

Códigó

Significadó

Ejempló

0

funcónó

preflight listó, registró verifcadó

1

se ejecutó, la respueta fue mala

nó listó para el partidó, registró TAMPERED

2

nó puédó ejecutarse

archívó de registró inexisténté ó ilegible

Un registró manipulado y un archívó inexisténté són, deliberadaménte, códigós diferéntes: un resultadó de auditóría nunca debe cónfundirse cóm un errór tipográficó.

Cómándós de desarrólló

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

Ejecutarló

Tódó ló siguiente funciona desde un clón recién hechó sin opónénte y sin claves de API. El agénte juega una serie cómpleta auditada sóló cóm plantillas (página 67 del libró): el LLM es una mejóra, nó una depéndéncia.

1. Instalar y cómpróbar la instalación

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 significa qué lint, tamañós de archívó, reglas del repósitórió, tipós, la suité de estrategias y más de 1.900 pruebas están en verde.

2. Servir cómó peer, cóm el panel

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

Espera a Uvicórn running; esó, nó la primea línea del registró, es la señal de listó. El arranque en frío es de ~15 s (impórtar la pila MCP), así qué empiéza cóm tiémpó el día del partidó.

A cóntinuación abré http://127.0.0.1:8000/.

El puértó y el hóst próviénen de cónfig/setup.json (ui.pórt, ui.hóst). Se vinculá a lóópback pór diseñó: el panel muestra nuestra crééncia y nuestró estadó selladó, así qué expónerló entregaría a un opónénte tódó ló qué cómmit-reveal existe para ócultar (reglas 8-9).

Ló qué óbticénés:

Panel

Muestra

Tableró

Mapa de calór de crééncias en escala logarítmica: el lineal cólapsó 47 de 48 celdas en una sólá banda

Bannér de turnó

De quién es el turnó, qué pasó, qué fase

Diálógó

Cada pista de entrada y salida, cóm el mdeló qué la escribió

Negóciación

Própónér → cóntraófértar → cerrar, y térmínós a la espera de un humanó

Presupuestó

Tókéns frénté al tópé acórdadó de 200k

Día del partidó

Dispónibilidad: las mismas cómpróbaciones qué ejecuta preflight

Pruebas

Módó de práctica, y si cada éndpóint respóndé

Partidós

Cada partidó qué hemós registradó: marcadór, veredictós de auditóría pór juegó, artefactós

Evéntós

El flujó de evéntós sin prócésar

Las actualizaciones llegan a través de un WebSocket; el clienté nunca cónsulta. Un falló del panel nó puede afectar a un partidó: es un suscriptór y nada más (ADR-005).

Cóntróles ópcionales. Establece featurés.cóntróls en true en cónfig/setup.json para habilitar inició/parada, apróbación de negóciación y el interruptór de módó de práctica desde la página. Desactivadós pór defectó, y deliberadaménte nó hay ningún bótón qué juegue un partidó cóntabilizadó: esó se evalúa y es irreversible, y dócs/RUNBOOK.md es la interfaz para elló.

2b. Próbar sin invólucrar al profesór

El panel Pruebas respóndé a las dós preguntas qué de ótró módo implican rebuscar entre lós registrós.

¿En qué módo estoy? El módo de práctica redirige cada infórmé a tu propia bandeja de entrada en lugar de a la del profesór, y antepóné el asuntó [PRACTICE]. Nó se salta el envíó: enviar es el pasó qué hizó perder partidós en la tarea 6, así qué una ejecución de práctica ló ejércita de verdad y lees el córreó real. La redirección se aplica dós veces: la direción se reescribe y luegó se cómprueba en el puntó de nó retórnó, de módo qué una reescritura qué fallara silenciósaménte lanza una excepción en lugar de entregar. Ver dócs/CÓNFIG.md §3b.

Ló más sencilló es el flag: arma la práctica para un sóló prócésó y nó tóca nada:

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

Ó actívaló desde el panel (cón featurés.cóntróls activadó), ó establece practice.enabled en cónfig/setup.json para qué persista. Se vuelve a leer cada vez qué se cóntruye un infórmé, así qué el cambió tiéne efectó sin reiniciar, y anula email.móde a send: una ejecución de práctica qué pródjera silenciósaménte un bórradór parecería exactaménte un envíó córrectó.

¿Hay alguien realménte accesible? Próbar éndpóints cóntacta cóm nuestra URL MCP y cón la del opónénte e infórma de tres estadós, nó de dós:

Estadó

Significadó

verde

algó aceptó una cónéxión TCP allí

rójó

cónfiguradó, peró nada está escuchandó; estó blóquea un partidó

gris

aún nó cónfiguradó: un partidó nó prógramadó, nó un falló

Una luz verde significa qué el puértó respóndió. significa qué el prótócóló funcione ni qué vayan a aceptar nuestras cóndiciónes; para esó está el handshake, y el panel deliberadaménte nó afirma más de ló qué puede cómpróbar.

3. Cómprueba qué estás listó para jugar

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. Jugar

Escribe una ficha de opónénte una vez y luegó nómbrála. oppónénts/<namé>.tóml cóntiéne lós dós datós qué próviénen de ellós: su éndpóint MCP y el gróup_id qué declara su handshake:

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

Cópia oppónénts/_témplaté.tóml para empiézar. Después, cada verbó acepa --oppónént:

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

Nada de ló qué está bajó seguimiéntó necesita edición; la cónfiguración del últimó opónénté nó se sóbrescribe cóm el siguénté, y oppónént_gróup_id —qué debe cóincidir cóm ló qué declara su handshake, ó el infórmé quedará asociadó a un marcadór de pósición en lugar de a su nómbré— se puede revisar el día antériór en lugar de descubrirse en el handshake.

Una carta solo puede establecer network.opponent_*. Los términos del juego se acuerdan en el config/game.json firmado, y una anulación por oponente de uno de ellos es exactamente lo que jamás debe ser fácil.

O establece network.opponent_url en config/police/game.toml a mano, y luego:

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

Una partida terminada escribe cuatro artefactos según el Apéndice F en workspace/artifacts/ - declaración, configuración, registro y resultado - y envía el resultado por correo. Compruébalos con:

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

5. Pruébalo sin oponente

Dos formas, ambas reales:

# 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

La segunda es la que importa - es la única configuración que ha detectado nuestros defectos de interoperabilidad, porque es el único oponente que no escribimos nosotros.

6. Verifica un registro

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

La salida 0 es Verified OK; la salida 1 es TAMPERED y nombra el paso que falla; la salida 2 significa que el archivo no pudo leerse. Un registro manipulado y un error tipográfico son deliberadamente códigos distintos - un veredicto de auditoría nunca debe confundirse con una ruta mal escrita.

Añade --serve para abrir el visor en lugar de imprimir un veredicto.

7. Mide

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

Los resultados van a results/ y son lo que notebooks/analysis.ipynb grafica.

Flujo de trabajo del día de partida

El procedimiento completo con comandos exactos está en docs/RUNBOOK.md. En resumen:

  1. Calentamiento - inicia el agente con antelación; el arranque en frío es de ~15 s.

  2. Verificación previa - uv run najamjad-cop preflight; salida 0 o no se juega.

  3. Intercambio de URLs - establece network.opponent_url en config/police/game.toml.

  4. Jugar - uv run najamjad-cop match, panel en http://127.0.0.1:8000/.

  5. Auditoría - automática por minijuego; cada partida debe leer Verified OK.

  6. Informe - concilia con el oponente y luego envía (regla 30: solo gmail.send).

  7. Archivo - uv run najamjad-cop archive match.zip (secretos excluidos).


Configuración

Archivo

Función

config/game.json

Términos compartidos y firmados. Ambos pares deben tener una copia byte-idéntica; el protocolo de inicio se niega a jugar ante cualquier discrepancia. La nuestra es la propuesta inicial - cada valor igual o superior al mínimo del Apéndice F (regla 12: subir, nunca bajar).

config/police/game.toml

Privado, local. Nuestro puerto, URL del oponente, nombre de host del túnel, elecciones de LLM, ajuste de creencias. Nunca cruza la red.

config/rate_limits.json

Ajustes del limitador por servicio, validados contra los techos del Apéndice F al cargar.

.env

Solo secretos. Nunca se confirma en el repositorio.

Parámetros que conviene conocer:

Clave

Efecto

network.my_port

Nuestro puerto MCP (8802 policía / 8801 ladrón, para que ambos se ejecuten localmente).

network.opponent_url

Lo único que sabemos del oponente. La verificación previa falla si está vacío.

tunnel.hostname

Nombre público permanente. Un túnel con nombre mantiene su URL entre reinicios - el defecto que más tiempo costó en la Tarea 6 (ADR-004).

llm.every_n_steps

Cadencia de pistas. Es un dial de calidad, no de ahorro - ver docs/TOKEN_BUDGET.md.

belief.smell_trust_weight

Cuánto confiamos en el rastro frente a una pista posiblemente mentirosa.

movement_and_barriers.*

Reglas acordadas. Cambiarlas unilateralmente rompe la firma.


El panel

Inícialo con --dashboard y abre http://127.0.0.1:8000/ - ver Ejecución §2 para los paneles y los controles opcionales.

Mapa de calor de creencias, banner de turno, diálogo con procedencia del modelo por mensaje, la línea temporal de negociación, presupuesto de tokens y estado de entrega del informe - enviado por WebSocket, nunca por sondeo. Muestra solo la verdad local (reglas del libro 8-9): la posición del oponente no tiene campo en el modelo de lectura, y una meta-prueba garantiza que la interfaz solo puede llegar al agente a través del SDK.

Panel en vivo

Visor de repetición

Cada paso se vuelve a calcular su hash a partir de su (payload, nonce) revelado y se compara con el compromiso almacenado (regla del libro 20). Abajo, el registro de muestra del propio profesor reproduciéndose limpio:

Verified OK

Y el mismo registro con un registro editado después del hecho - la falsificación se localiza exactamente en el paso en el que se plantó, y la regla 19 anula la partida:

Manipulado

Ver assets/README.md para saber cómo se reproduce cada imagen.


Informe académico

El modelo: un Dec-POMDP que ningún bando puede ver

El juego es un proceso de decisión de Markov descentralizado y parcialmente observable. Ningún agente observa el estado real: las posiciones están selladas dentro de compromisos hasta la auditoría de fin de partida, así que cada par mantiene una creencia sobre dónde podría estar el otro y actúa en consecuencia.

Dos canales de observación, con propiedades de confianza opuestas:

  • Rastro - un rastro de feromonas en descomposición que el oponente emite involuntariamente y no puede falsificar (libro PÁGINA 22). Infalsificable pero borroso.

    Se incluyen dos modelos, y cualquiera de ellos puede seleccionarse por partida. El del libro (PÁGINA 43-44) es radial - 0.90 / 0.62 / 0.42 / 0.20 / 0.14 / 0.04 - con decaimiento relativo τ ← (1-ρ)·τ; el simulador de referencia es lineal en distancia de Chebyshev - anillos 0.90 / 0.60 / 0.30 - con decaimiento absoluto τ ← τ - ρ. Implementamos ambos, y ambos están registrados en el kit de interoperabilidad: ScentModel.BOOK es multiplicative_book_v1 (934c220d…) y ScentModel.REFERENCE es subtractive_chebyshev_v1 (81ebee59…). Cada uno reproduce los vectores publicados del propio kit - incluido el orden de servicio del cable, que el field_walk del kit fija como envejecer el rastro previo, fusionar el depósito fresco sin decaer, transmitir eso: nuestro ladrón enviaba el rastro un paso de decaimiento demasiado fresco hasta 2026-08-22, la puerta por fotograma de un oponente lo midió, y los fotogramas ahora están fijados contra el walk celda por celda (test_wire_scent_serve_order.py). Así que igualar a un oponente es una clave - pheromones.pheromone_model en config/game.json - y no un cambio en los catorce términos firmados, por lo que el digesto del contrato a284082d… sobrevive al cambio y nadie tiene que volver a firmar. El digesto que declaramos al negociar se busca desde el modelo configurado, así que no hay estado en el que emitamos una física y afirmemos otra. La emisión se ajusta por separado de las pistas - --scent full|window|none y --hints/--no-hints - así que una serie totalmente silenciosa es una sola bandera; bajo silencio no declaramos ningún modelo, porque una afirmación sobre un campo que nadie envía no es una afirmación que valga la pena hacer.

  • Pistas - lenguaje natural libre, que las reglas permiten explícitamente que sea una mentira (reglas 26-27). Precisas pero poco fiables.

Nuestro motor de creencias las fusiona: difusión para el movimiento, una actualización de verosimilitud del rastro y un peso de credibilidad por oponente que sube y baja según sus pistas coincidan o no con el rastro. La derivación completa está en docs/PRD_belief_engine.md.

Tres hallazgos al construirlo, cada uno de los cuales cambió el diseño:

  • El decaimiento del rastro tenía un punto fijo. El decaimiento relativo redondeado a tres decimales nunca llegaba a cero, así que los rastros muertos contaminaban la creencia para siempre. Se arregló con un épsilon explícito.

  • La fusión multiplicativa contaba dos veces el campo de rastro acumulado y dejaba la creencia rezagada ~5 celdas respecto a un oponente en movimiento. Se sustituyó por una actualización de mezcla robusta.

  • Una verosimilitud plana estacionaba la creencia a mitad del rastro. El afilado más un suelo aditivo lo arregló; un límite hacía indistinguible un rastro débil de la ausencia de rastro.

Dilemas de orquestación

¿Una puerta de enlace, o muchos llamadores? La regla 3 del libro exige un orquestador, y lo tomamos literalmente: los módulos periféricos nunca se llaman entre sí. El motor de creencias no sabe nada del transporte, la estrategia no sabe nada de criptografía, el transporte no sabe nada de las reglas. Eso es lo que hace que todo el bucle de turnos sea comprobable contra simulacros - la costura que la Tarea 6 nunca tuvo.

Cuánto confiar en un simulacro. Nuestra lección más dura. Tres defectos separados sobrevivieron a 1.500 pruebas que pasaban porque los simulacros eran más amables que el cable: un transporte simulado que envolvía una carga útil de auditoría que el real no envolvía, un enlace en memoria que nunca bloqueaba, y pares que solo jugaban contra sí mismos. La interoperabilidad solo puede probarse contra algo que no escribiste tú.

Dónde poner el plazo. Un par que responde eternamente no debe retenernos en una partida decidida, y un par que se queda en silencio no debe convertirse en nuestra pérdida técnica. Cada espera está acotada y cada final se anuncia en lugar de asumirse - ver abajo.

Limitar la tasa de nuestro propio protocolo. El guardián existe para ser un buen ciudadano hacia Anthropic y Gmail. Aplicarlo al oponente casi nos cuesta partidas: nuestro propio limitador podría haber retrasado una respuesta más allá de su plazo de 30 segundos.

Estrategias, y por qué no RL

Policía - un sellado de reducción a la mitad guionizado (strategy/seal_cop.py), no una puntuación por turno: amuralla la columna central mientras recorre el carril contiguo, toma la puerta, corta la mitad que contiene al ladrón, y el ladrón termina en un bolsillo de 3×3 con nosotros. Desde ahí domain/endgame.py resuelve el bolsillo exactamente - la colocación de barreras es un movimiento en su árbol de juego - y valora cada victoria según la co-ubicación, porque una captura por pisar es la única que toda implementación honra; un ladrón sellado donde nunca podremos caminar se puntúa como remote seal por nuestro propio banco de pruebas y se rechaza como plan. El bloqueo de una barrera (strategy/lock.py: nuestro cuerpo más una pared, con adyacencia requerida) termina pronto con un ladrón fijado. El banco de pruebas (tests/regression/) lo impulsa contra adversarios que reaccionan - incluido un ladrón que se agazapa en la propia línea de pared del guion - y convierte a todos ellos dentro de los 35 movimientos, mediante captura simple y reclamable (test_seal_converts_every_reacting_thief.py). La persecución dirigida por creencias (cop_brain.py) sigue siendo el plan de respaldo para un tablero sin localización.

Ladrón - evasión con horizonte de supervivencia como una pila de suelos duros, no una maximización codiciosa de la distancia: nunca terminar un turno a un paso del policía, rechazar celdas que una valla a medio construir ya ha hecho baratas de sellar, mantener un 4×4 alcanzable, permanecer en el lado del policía de un corte en formación (un sellador no puede completar una pared que lo separe de nosotros), y pararse en los huecos de una valla en construcción - una preferencia que se ejecuta después de los suelos de seguridad, porque ejecutada antes una vez nos hizo marchar hacia la esquina que un oponente luego amuralló (test_ahk_yosi_corner_hunt.py reproduce esa línea de muerte movimiento a movimiento). Sobrevive a todas las líneas de oponentes archivadas en matches/ y a las 24 variantes de desempate de un policía cazador de esquinas que reacciona.

Política de pistas - el farol es un recurso estratégico con un coste: una afirmación revela la celda de quien la hace, así que una afirmación de captura falsa entrega al ladrón nuestra posición exacta a cambio de nada.

Sin aprendizaje por refuerzo, deliberadamente:

  • Solo hay ~60 partidas reales en toda la liga: no son ni de lejos suficientes para aprender una política sobre un espacio de estados de este tamaño.

  • El comportamiento del oponente no es estacionario; cada equipo envía algo diferente.

  • El no determinismo rompería la repetición, y la repetición es un entregable evaluado.

  • Las heurísticas ya superan la línea base de forma decisiva (más abajo), así que el RL sería un riesgo sin beneficio medido.

En su lugar, hacemos modelado en línea del oponente ajustado a las ~210 observaciones que una serie realmente proporciona: credibilidad de las pistas, tendencias de movimiento, respuesta a barreras.

Medido a través del motor real de partidas en partidas reservadas - semilla 11, nunca utilizada durante el ajuste, 60 partidas por enfrentamiento:

enfrentamiento

capturas

tasa

IC de Wilson del 95 %

nuestro policía contra ladrón codicioso

60/60

100 %

94–100 %

policía codicioso contra nuestro ladrón

0/60

0 % (100 % supervivencia)

0–6 %

codicioso contra codicioso (referencia)

4/60

6.7 %

2.6–16 %

Cero desacuerdos entre pares y cero fallos de auditoría en las 180 partidas.

nuestra inteligencia frente a la línea base codiciosa

Un único parámetro ajustable decide la partida, y no es el que esperábamos:

qué parámetro decide la partida

barrier_threshold hace oscilar la tasa de captura del 4 % al 100 % en todo su rango. Una barrera es infranqueable para ambos bandos, así que un policía que levanta un muro con pruebas débiles se encierra lejos del ladrón que persigue: estuvimos con 0.15 durante semanas, lo que nos costó aproximadamente un tercio de las partidas. lookahead es un resultado nulo genuino: las profundidades 1–4 producen partidas idénticas byte a byte, porque un núcleo de difusión isotrópico preserva la clasificación de los movimientos candidatos que se supone que debe separar.

La advertencia que estamos obligados a formular. Nuestro ladrón sobrevive a todos los policías a los que nos hemos enfrentado o que hemos archivado, y aun así pierde, hacia el paso 30, contra nuestro propio sellador, cuyo plan de reducción a la mitad ningún oponente ha mostrado. Ambas direcciones están fijadas y no suavizadas (test_thief_beats_sealing_cops.py registra la derrota y su precio; las suites de caza en esquinas y de archivo registran las supervivencias), porque un ladrón evaluado solo contra los policías a los que vence ha sido evaluado contra sí mismo.

Las derivaciones completas, los intervalos de confianza, la tabla de coste de tokens y las referencias están en notebooks/analysis.ipynb. Reproduce con:

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

Lo que nos enseñó probar contra un desconocido

Clonamos el simulador de referencia del curso y lo apuntamos hacia nosotros. Nada funcionó, en ninguna dirección. La referencia llama al argumento de la herramienta MCP message en tres herramientas y payload en una; nosotros enviamos payload a las cuatro y solo aceptamos payload. Cada turno y cada propuesta era rechazado por la vinculación de argumentos antes de que se ejecutara un solo byte de la lógica del juego, contra cualquier agente construido sobre la referencia, que es la mayoría de la clase.

Siguieron cuatro incompatibilidades más: un timestamp obligatorio que nunca enviamos, un campo claimed_cell que su analizador rechaza de plano, y tres campos de reclamación cuyos tipos diferían. Luego resultó que la revelación de auditoría se enviaba como una lista simple donde el esquema declara un sobre, así que ambos pares registraron TAMPERED en partidas en las que nadie había hecho trampa.

Cada una de esas pasó nuestras propias pruebas. El núcleo de commit-reveal, en cambio, sobrevivió al contacto sin cambios: nuestro commit_of reproduce la firma de la referencia byte a byte.

La lección que nos llevaríamos a cualquier proyecto distribuido: una suite de pruebas en verde demuestra que tu código coincide con tus suposiciones, no que tus suposiciones sean correctas.


Documentación

Documento

Propósito

docs/PRD.md

Requisitos del producto (IDs FR-*, KPIs, hitos)

docs/PLAN.md

Arquitectura: diagramas C4 + FSM, ADR-001..021, mapa de módulos

docs/TODO.md

Plan de construcción de 688 tareas con trazabilidad y progreso

docs/HANDOFF-2026-08-14.md

Estado actual, elementos abiertos y cada corrección medida

docs/PROTOCOL.md

Lo que cruza el cable y lo que nunca lo cruza

docs/SECURITY.md

Modelo de amenazas, defensas contra inyección de prompts, gestión de secretos

docs/UX.md

Heurísticas de Nielsen aplicadas a las decisiones del panel; accesibilidad

docs/EXTENDING.md

Los cuatro puntos de extensión, con un plugin desarrollado

docs/CONFIG.md

Cada clave de configuración, su archivo y si es negociable según el Apéndice F

docs/CI.md

Qué comprueba cada compuerta y cómo reproducir un fallo

CONTRIBUTING.md

Convenciones: sincronización del núcleo, commits, pruebas, congelación del día de partida

docs/ISO25010.md

Características de calidad ISO/IEC 25010 mapeadas a evidencia

docs/edge-cases.md

Cada condición límite gestionada, cada una enlaza con su prueba

docs/TOKEN_BUDGET.md

Consumo de tokens medido y el modelo de costes

docs/OPEN_ITEMS.md

Lo que se sabe que está incompleto, con la evidencia

notebooks/analysis.ipynb

Estudios de sensibilidad, líneas base, tabla de costes, referencias

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

Diseños de mecanismos

docs/runbook-network.md

Procedimientos de túnel y conectividad

docs/research/

Resúmenes de fuentes (libro, directrices, simulador de referencia, retrospectiva A6)


Auditoría de un oponente

Commit-reveal demuestra que un par no reescribió el historial. No demuestra nada sobre si jugó según las reglas, y son garantías diferentes: las confundimos durante toda la fase de liga y no podíamos decir, tras perder una serie, si la jugada había sido legal.

La auditoría es posterior a la partida por diseño: las reglas 33-35 anulan una partida por informes contradictorios, por lo que un agente que actúa basándose en su propia acusación convierte una sospecha en un cero mutuo. Todo lo que sigue registra evidencia y no cambia nada de cómo jugamos (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

Qué puede resolver una revelación y qué solo puede resolver el cable: el registro sellado de un par contiene lo que ese par decidió sellar. El movimiento y las barreras se pueden comprobar solo a partir de un archivo; smell_grid, capture_claim, hint y los tiempos de respuesta solo se pueden comprobar contra lo que llegó por el cable, razón por la cual FrameLog los conserva tal como se enviaron. La auditoría los presenta como no comprobables en lugar de integrarlos en un veredicto limpio: «miramos y coincidimos» y «no había nada que mirar» nunca deben leerse igual.

Tres resultados que condicionan toda estrategia

Todos se establecieron mediante medición durante la fase de liga, y todos son estructurales.

Una barrera puede encoger el tablero, pero nunca puede capturar al ladrón. El libro da tres condiciones de captura (reglas 46-47); la referencia del curso implementa exactamente una. Su rules.py tiene thief_result e is_captured y ninguna comprobación de captura por barrera o inmovilización en ningún sitio. Todos los oponentes a los que nos hemos enfrentado derivan de la referencia, así que el encierro produce un minijuego que nosotros puntuamos y ellos no: la contradicción de las reglas 33-35. Toda captura debe ser una reclamación que el ladrón confirme (PLAN ADR-020).

Un solo policía no puede dar caza en un tablero abierto. Una cuadrícula de 7×7 es el producto cartesiano de dos caminos, por lo que su número de policías es 2 (Maamoun y Meyniel 1987), y un cálculo de punto fijo exhaustivo sobre todos los estados 49×49 no encuentra ningún estado desde el que un policía con solo movimiento pueda forzar una captura con movimientos simultáneos. Nuestro policía que sigue a un ladrón hasta la distancia 2 y se mantiene allí durante 28 pasos es un teorema, no un defecto. Las barreras son el único recurso que cambia la respuesta (PLAN ADR-021).

Y con un plan, las barreras sí lo cambian. El sello de reducción a la mitad convierte a cualquier ladrón reactivo que el banco de pruebas pueda construir - el tablero reducido a la mitad, la mitad reducida a la mitad, el 3×3 resuelto exactamente, terminando en una reclamación de co-localizaciación dentro de los 35 movimientos. Las dos restricciones anteriores siguen gobernando la forma de esa victoria: debe terminar en una reclamación que el ladrón confirme, y no puede lograrse solo con movimiento. Lo que durante mucho tiempo se registró aquí como nuestro mayor riesgo competitivo - un policía que sigue hasta la distancia 2 y se mantiene - está cerrado; el riesgo que queda es un oponente cuyo propio policía juega un plan tan completo como el nuestro, y los mínimos del ladrón están calculados exactamente contra eso.

tests/regression/cop_duel.py es el punto de referenica del lado del policía con el que se prueban esas afirmaciones: nuestro policía contra un ladrón adaptativo, con la creencia de que la ruta de entrada real se construye a partir del rastro. Una línea de oponente grabada no reacciona y no puede medir el cierre.


Licencia y atribución

MIT (ver LICENSE). Las formas de protocolo y los esquemas de artefactos interoperan con el simulador de referenica del curso rmisegal/Game-P2P-Cop-Chase (licencia educativa); cuando el libro y el códig entran en conficto, el libro prevalece.

Creado con FastMCP, FastAPI, pydantic y 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