Skip to main content
Glama
najikay

najamjad-cop

by najikay

Pursuit League - Cop Agent 👮

Die Polizei-/Cop-Seite eines Zweipersonen-Abschlussprojekts für Orchestration of AI Agents: ein verteiltes Polizisten-und-Diebe-Spiel, das Peer-to-Peer über MCP (FastMCP/HTTP) gegen die Agents anderer Teams gespielt wird, mit SHA-256-Commit-Reveal-Integrität und Gmail-API-Ergebnisübermittlung.

Begleit-Repository (Dieb-Agent): https://github.com/najikay/pursuit-thief-agent Das gemeinsame Kernpaket ist byte-identisch in beiden Repos, erzwungen durch scripts/sync_core.py in CI (siehe docs/PLAN.md, ADR-002).

ci

Team: Naji Kayal · Amjad Abed Status: M6 – sechs gewertete Serien gespielt und eingereicht gegen sechs verschiedene Gegner; die Bestehensgrenze aus Regel 31 wurde dreimal übertroffen. Spielt eine vollständig auditierte Serie, interoperiert mit dem Referenzsimulator des Kurses und mit sechs unabhängigen Teamimplementierungen und auditiert nach dem Match das Spiel eines Gegners. Verbleibende Arbeit ist in docs/TODO.md erfasst.

Liga-Bilanz

#

Datum

Gegner

Ergebnis

Wir

Gegner

1

2026-08-08

uoh-ay26

gewonnen 6–0

90

30

2

2026-08-13

imreeyal

gewonnen 6–0

90

30

3

2026-08-14

vibecode

verloren 0–6

30

90

4

2026-08-17

MOAAMOHA

gewonnen 4–2

60

40

5

2026-08-18

nis-yar1

verloren 0–6

30

90

6

2026-08-21

ahk-yosi

unentschieden 3–3

75

75

Sechsunddreißig von sechsunddreißig Minispielen wurden bei der Prüfung als Verified OK bestätigt, über sechs gewertete Serien, mit null technischen Niederlagen, die uns zuzuschreiben wären. Das ist die Zahl, die wir zuerst nennen würden: Jedes Spiel, das wir gespielt haben, war eines, das beide Seiten erneut durchgehen und akzeptieren konnten, auch die beiden, die wir deutlich verloren haben.

Zwei Serien wurden Feld für Feld mit dem eigenen eingereichten Bericht des Gegners abgeglichen – die von vibecode bei 66 Feldern mit null Abweichungen, und ein späteres Freundschaftsspiel gegen anrbj666, das in allen sechs Teilspielen, beiden mutual_agreement.sha256-Werten und beiden github_commit-Paaren pro Fenster übereinstimmte. Ein Bericht, der mit dem des Gegners übereinstimmt, ist die einzige Art, die gemäß Regeln 33–35 nicht für ungültig erklärt werden kann, und er ist uns mehr wert als ein Spielstand.


Inhaltsverzeichnis


Related MCP server: Police MCP Server

Installation

Anforderungen

Python

3.12+ (von uv verwaltet – du musst es nicht vorinstallieren)

uv

der einzige hier verwendete Paketmanager (Richtlinien §8.4)

OS

Linux, macOS oder Windows über WSL2 – entwickelt unter WSL2

Optional

cloudflared für einen öffentlichen Tunnel; ein Google-Cloud-Projekt für Berichts-E-Mails

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

Ein erfolgreicher Lauf von check_all.py bedeutet, dass die Installation solide ist: Linting, Dateigrößen-Limits, Repo-Regeln, Typprüfung und die vollständige Testsuite.

Geheimnisse – kopiere .env-example nach .env und trage echte Werte ein. Es wird nie etwas Geheimes committet; .gitignore deckt .env, secrets/, token.json und credentials.json ab, und ein CI-Gate lässt den Build fehlschlagen, falls je eines versioniert wird (Buchregeln 39–40).

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

Fehlerbehebung

Symptom

Ursache und Behebung

port 8802 … already in use

Ein anderer Agent läuft. Beende ihn oder ändere network.my_port in config/police/game.toml.

peer braucht ~15 s zum Antworten

Normal: der Import des MCP-Stacks. Er gibt Uvicorn running aus, wenn er wirklich bereit ist – starte gut vor einem Match.

Gegner meldet, dass wir nicht erreichbar sind

Prüfe den Tunnel: uv run najamjad-cop preflight. Ein 502 bedeutet, dass der Agent nicht läuft; ein JSON-RPC-Fehler wie "Client must accept text/event-stream" bedeutet, dass er funktionsfähig ist – das ist ein Browser, der einen MCP-Endpunkt aufruft.

Failed to spawn: najamjad-cop

Aus dem Repository-Root ausführen; das Konsolenskript liegt in .venv dieses Repos.

OAuth-Browser öffnet sich nicht (WSL)

Erwartet – WSL hat keinen Standardbrowser. Verwende uv run python scripts/authorise_gmail.py --manual und füge die URL selbst ein.

Alles ist auf /mnt/c langsam

Windows-gemountete Dateisysteme sind in WSL langsam. Das Ereignisprotokoll hält sein Handle genau aus diesem Grund offen; wenn möglich, behalte den Arbeitsbereich im Linux-Dateisystem.


Kommandozeile

Ein Konsolenskript pro Repo (najamjad-cop hier, najamjad-thief im Begleit-Repo). Jedes Verb besteht aus Argument-Parsing plus einem einzigen SDK-Aufruf – die CLI enthält keine Spiellogik, und ein Meta-Test stellt sicher, dass das so bleibt.

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)

Exit-Codes, da diese in Skripten laufen:

Code

Bedeutung

Beispiel

0

es hat funktioniert

Preflight bereit, Log verifiziert

1

es lief, die Antwort war schlecht

nicht matchbereit, Log TAMPERED

2

es konnte nicht ausgeführt werden

Log-Datei fehlt oder nicht lesbar

Ein manipuliertes Log und eine fehlende Datei sind absichtlich verschiedene Codes: Ein Prüfergebnis darf nie mit einem Tippfehler verwechselt werden.

Entwicklungsbefehle

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

Ausführen

Alles unten funktioniert mit einem frischen Klon ohne Gegner und ohne API-Schlüssel. Der Agent spielt eine vollständig auditierte Serie allein auf Basis von Vorlagen (Buch Seite 67) – das LLM ist eine Verbesserung, keine Abhängigkeit.

1. Installieren und die Installation belegen

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 bedeutet, dass Linting, Dateigrößen, Repo-Regeln, Typen, die Strategie-Suite und über 1.900 Tests grün sind.

2. Als Peer bereitstehen, mit dem Dashboard

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

Warte auf Uvicorn running – das, nicht die erste Log-Zeile, ist Bereitschaft. Der Kaltstart dauert ~15 s (Import des MCP-Stacks), also starte am Spieltag früh.

Öffne dann http://127.0.0.1:8000/.

Port und Host stammen aus config/setup.json (ui.port, ui.host). Es bindet bewusst an Loopback: Das Dashboard zeigt unsere Überzeugung und unseren versiegelten Zustand; es offenzulegen würde einem Gegner alles in die Hand geben, was Commit-Reveal verbergen soll (Regeln 8–9).

Was du bekommst:

Panel

Zeigt

Board

Glaubens-Heatmap auf logarithmischer Skala – linear kollabierten 47 von 48 Zellen in ein Band

Turn banner

Wessen Zug, welcher Schritt, welche Phase

Dialogue

Jeder Hinweis rein und raus, mit dem Modell, das ihn geschrieben hat

Negotiation

Vorschlagen → Kontern → Sperren sowie Bedingungen, die auf einen Menschen warten

Budget

Tokens gegen die vereinbarte 200k-Obergrenze

Match day

Bereitschaft – dieselben Prüfungen, die preflight ausführt

Testing

Übungsmodus und ob jeder Endpunkt antwortet

Matches

Jedes eingereichte Match: Punktestand, Audit-Urteile pro Spiel, Artefakte

Events

Der rohe Ereignisstrom

Updates kommen über ein WebSocket; der Client fragt nie ab. Ein Dashboard-Ausfall kann ein Spiel nicht beeinflussen – es ist ein Abonnent und sonst nichts (ADR-005).

Optionale Bedienelemente. Setze features.controls in config/setup.json auf true, um Start/Stopp, Verhandlungsfreigabe und den Übungsmodus-Schalter von der Seite aus zu aktivieren. Standardmäßig aus, und es gibt bewusst keinen Knopf, der ein gewertetes Match spielt – das ist benotet und unumkehrbar, und docs/RUNBOOK.md ist die Schnittstelle dafür.

2b. Testen, ohne den Dozenten zu bemühen

Das Testing-Panel beantwortet die zwei Fragen, die sonst das Wühlen in Logs bedeuten.

In welchem Modus bin ich? Der Übungsmodus leitet jeden Bericht an dein eigenes Postfach um statt an das des Dozenten und versieht den Betreff mit dem Präfix [PRACTICE]. Er überspringt das Senden nicht – Senden ist der Schritt, der in Aufgabe 6 Matches verloren hat, daher übt ein Übungslauf das wirklich und du liest die tatsächliche E-Mail. Die Umleitung wird doppelt erzwungen: Die Adresse wird umgeschrieben und dann an dem Punkt geprüft, an dem es kein Zurück mehr gibt, sodass eine still fehlgeschlagene Umschreibung eine Ausnahme auslöst, statt zuzustellen. Siehe docs/CONFIG.md §3b.

Am einfachsten ist das Flag – es schaltet den Übungsmodus für einen Prozess scharf und fasst nichts an:

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

Oder schalte es im Panel um (bei aktiviertem features.controls), oder setze practice.enabled in config/setup.json, um es dauerhaft zu machen. Es wird bei jedem Erstellen eines Berichts neu gelesen, sodass der Wechsel ohne Neustart wirkt, und es überschreibt email.mode mit send – ein Übungslauf, der still einen Entwurf erzeugte, würde genauso aussehen wie ein erfolgreicher Versand.

Ist überhaupt jemand erreichbar? Probe endpoints wählt unsere MCP-URL und die des Gegners an und meldet drei Zustände, nicht zwei:

Zustand

Bedeutung

grün

dort hat etwas eine TCP-Verbindung akzeptiert

rot

konfiguriert, aber es lauscht nichts – das blockiert ein Match

grau

noch nicht konfiguriert – kein Match geplant, kein Fehler

Ein grünes Licht bedeutet, dass der Port geantwortet hat. Es bedeutet nicht, dass das Protokoll funktioniert oder dass sie unseren Bedingungen zustimmen werden – dafür ist der Handshake da, und das Panel behauptet bewusst nicht mehr, als es prüfen kann.

3. Prüfen, ob du zum Spielen bereit bist

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

Schreibe einmal eine Gegnerkarte, dann benenne sie. opponents/<name>.toml enthält die zwei Fakten, die von ihnen stammen – ihren MCP-Endpunkt und die group_id, die ihr Handshake deklariert:

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

Kopiere opponents/_template.toml, um zu starten. Danach akzeptiert jedes Verb --opponent:

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

Nichts Versioniertes muss bearbeitet werden, die Einstellungen des letzten Gegners werden nicht vom nächsten überschrieben, und opponent_group_id – die dem entsprechen muss, was ihr Handshake deklariert, sonst wird der Bericht unter einem Platzhalter statt unter ihrem Namen geführt – ist am Tag davor überprüfbar statt erst beim Handshake entdeckbar.

Eine Karte kann nur network.opponent_* setzen. Die Spielbedingungen sind im signierten config/game.json vereinbart, und eine gegnerindividuelle Übersteuerung einer einzelnen Bedingung ist genau das, was niemals einfach möglich sein darf.

Oder setze network.opponent_url von Hand in config/police/game.toml, dann:

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

Ein abgeschlossenes Match schreibt vier Artefakte gemäß Anhang F in workspace/artifacts/ – Deklaration, Konfiguration, Log und Ergebnis – und sendet das Ergebnis per E-Mail. Prüfe sie mit:

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

5. Ohne Gegner ausprobieren

Zwei Wege, beide real:

# 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

Der zweite ist der Entscheidende – es ist das einzige Setup, das jemals unsere Interop-Fehler gefunden hat, weil es der einzige Gegner ist, den wir nicht selbst geschrieben haben.

6. Ein Log verifizieren

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

Exit 0 bedeutet Verified OK; Exit 1 bedeutet TAMPERED und nennt den fehlgeschlagenen Schritt; Exit 2 bedeutet, dass die Datei nicht gelesen werden konnte. Ein manipuliertes Log und ein Tippfehler sind bewusst unterschiedliche Codes – ein Audit-Urteil darf niemals mit einem falsch geschriebenen Pfad verwechselt werden.

Füge --serve hinzu, um den Viewer zu öffnen, statt ein Urteil auszugeben.

7. Messen

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

Die Ergebnisse landen in results/ und sind das, was notebooks/analysis.ipynb plottet.

Ablauf am Spieltag

Das vollständige Verfahren mit exakten Befehlen steht in docs/RUNBOOK.md. In Kürze:

  1. Aufwärmen – den Agenten früh starten; Kaltstart dauert ~15 s.

  2. Preflightuv run najamjad-cop preflight; Exit 0 oder nicht spielen.

  3. URLs austauschennetwork.opponent_url in config/police/game.toml setzen.

  4. Spielenuv run najamjad-cop match, Dashboard auf http://127.0.0.1:8000/.

  5. Audit – automatisch pro Mini-Spiel; jedes Spiel muss Verified OK ergeben.

  6. Bericht – mit dem Gegner abgleichen, dann senden (Regel 30: nur gmail.send).

  7. Archivierenuv run najamjad-cop archive match.zip (Geheimnisse ausgeschlossen).


Konfiguration

Datei

Rolle

config/game.json

Gemeinsame, signierte Bedingungen. Beide Peers müssen eine byte-identische Kopie besitzen; der Handshake weigert sich, bei jeder Abweichung zu spielen. Unsere ist der Eröffnungsvorschlag – jeder Wert auf oder über dem Anhang-F-Minimum (Regel 12: erhöhen, nie senken).

config/police/game.toml

Privat, lokal. Unser Port, Gegner-URL, Tunnel-Hostname, LLM-Wahl, Belief-Tuning. Überquert niemals das Netzwerk.

config/rate_limits.json

Limiter-Einstellungen pro Dienst, beim Laden gegen die Anhang-F-Obergrenzen validiert.

.env

Nur Geheimnisse. Niemals einchecken.

Wissenswerte Parameter:

Schlüssel

Wirkung

network.my_port

Unser MCP-Port (8802 Cop / 8801 Thief, damit beide lokal laufen).

network.opponent_url

Das Einzige, was wir über den Gegner wissen. Preflight schlägt fehl, solange leer.

tunnel.hostname

Dauerhafter öffentlicher Name. Ein benannter Tunnel behält seine URL über Neustarts hinweg – der Fehler, der Assignment 6 am meisten Zeit gekostet hat (ADR-004).

llm.every_n_steps

Hinweis-Kadenz. Ein Qualitätsregler, kein Sparregler – siehe docs/TOKEN_BUDGET.md.

belief.smell_trust_weight

Wie sehr wir Duft gegen einen möglicherweise lügenden Hinweis vertrauen.

movement_and_barriers.*

Vereinbarte Regeln. Diese einseitig zu ändern bricht die Signatur.


Das Dashboard

Starte es mit --dashboard und öffne http://127.0.0.1:8000/ – siehe Running it §2 für die Panels und die optionalen Steuerungen.

Belief-Heatmap, Zug-Banner, Dialog mit Modell-Herkunft pro Nachricht, die Verhandlungs- Zeitleiste, Token-Budget und Berichtszustellstatus – per WebSocket gepusht, nie gepollt. Es zeigt nur lokale Wahrheit (Buchregeln 8-9): Die Position des Gegners hat kein Feld im Lesemodell, und ein Meta-Test erzwingt, dass die UI den Agenten nur über das SDK erreichen kann.

Live dashboard

Replay-Viewer

Jeder Schritt wird aus seinem offengelegten (payload, nonce) neu gehasht und mit dem gespeicherten Commitment verglichen (Buchregel 20). Unten das eigene Beispiellog des Dozenten, das sauber abspielt:

Verified OK

Und dasselbe Log mit einem nachträglich bearbeiteten Datensatz – die Fälschung wird genau auf den Schritt eingegrenzt, in dem sie platziert wurde, und Regel 19 macht das Spiel ungültig:

Tampered

Siehe assets/README.md dafür, wie jedes Bild reproduziert wird.


Akademischer Bericht

Das Modell: ein Dec-POMDP, den keine Seite sehen kann

Das Spiel ist ein dezentraler, teilweise beobachtbarer Markow-Entscheidungsprozess. Kein Agent beobachtet den wahren Zustand: Positionen sind bis zum Audit am Spielende in Commitments versiegelt, also hält jeder Peer eine Belief darüber, wo der andere sein könnte, und handelt danach.

Zwei Beobachtungskanäle mit gegensätzlichen Vertrauenseigenschaften:

  • Duft – eine zerfallende Pheromonspur, die der Gegner unfreiwillig abgibt und nicht fälschen kann (Buch SEITE 22). Nicht fälschbar, aber unscharf.

    Zwei Modelle werden ausgeliefert, und pro Match kann eines ausgewählt werden. Das Buch (SEITE 43-44) ist radial – 0,90 / 0,62 / 0,42 / 0,20 / 0,14 / 0,04 – mit relativem Zerfall τ ← (1-ρ)·τ; der Referenzsimulator ist linear in der Tschebyschow-Distanz – Ringe 0,90 / 0,60 / 0,30 – mit absolutem Zerfall τ ← τ - ρ. Wir implementieren beide, und beide sind im Interop-Kit registriert: ScentModel.BOOK ist multiplicative_book_v1 (934c220d…) und ScentModel.REFERENCE ist subtractive_chebyshev_v1 (81ebee59…). Jedes reproduziert die eigenen veröffentlichten Vektoren des Kits – einschließlich der Servierreihenfolge des Drahts, die das field_walk des Kits festlegt als alte Spur altern lassen, die frische Ablagerung unzerfallen einmischen, das übertragen: Unser Thief lieferte die Spur bis zum 22.08.2026 einen Zerfallsschritt zu frisch aus, ein per-Frame-Gate eines Gegners maß es, und die Frames sind jetzt zellenweise gegen den Walk gepinnt (test_wire_scent_serve_order.py). Also ist das Anpassen an einen Gegner ein Schlüssel – pheromones.pheromone_model in config/game.json – und keine Änderung an den vierzehn signierten Bedingungen, sodass der Vertrags-Digest a284082d… den Wechsel überlebt und niemand neu signieren muss. Der Digest, den wir bei negotiate deklarieren, wird aus dem konfigurierten Modell nachgeschlagen, sodass es keinen Zustand gibt, in dem wir eine Physik aussenden und eine andere behaupten. Emission wird separat von Hinweisen eingestellt – --scent full|window|none und --hints/--no-hints – sodass eine vollständig stille Serie ein einziges Flag ist; unter Stille deklarieren wir gar kein Modell, weil eine Behauptung über ein Feld, das niemand sendet, keine Behauptung ist, die sich zu machen lohnt.

  • Hinweise – freie natürliche Sprache, die die Regeln ausdrücklich erlauben zu lügen (Regeln 26-27). Präzise, aber unvertrauenswürdig.

Unsere Belief-Engine fusioniert sie: Diffusion für Bewegung, ein Duft-Wahrscheinlichkeits-Update und ein Glaubwürdigkeitsgewicht pro Gegner, das steigt und fällt, je nachdem ob ihre Hinweise mit der Spur übereinstimmen oder nicht. Die vollständige Herleitung steht in docs/PRD_belief_engine.md.

Drei Erkenntnisse aus dem Bau, von denen jede das Design verändert hat:

  • Duftzerfall hatte einen Fixpunkt. Relativer Zerfall, auf drei Dezimalstellen gerundet, erreichte nie null, also verschmutzten tote Spuren die Belief für immer. Behoben mit einem expliziten Epsilon.

  • Multiplikative Fusion zählte doppelt das kumulative Duftfeld und ließ die Belief einem bewegten Gegner um ~5 Zellen hinterherhinken. Ersetzt durch ein robustes Mischungs-Update.

  • Eine flache Likelihood parkte die Belief mitten in der Spur. Schärfen plus ein additiver Boden behob es; eine Klemme machte schwachen Duft ununterscheidbar von keinem.

Orchestrierungsdilemmata

Ein Gateway oder viele Aufrufer? Buchregel 3 schreibt einen Orchestrator vor, und wir haben das wörtlich genommen: Periphere Module rufen einander nie auf. Die Belief-Engine weiß nichts vom Transport, die Strategie weiß nichts von Krypto, der Transport weiß nichts von den Regeln. Genau das macht die gesamte Zugschleife gegen Fakes testbar – die Naht, die Assignment 6 nie hatte.

Wie viel man einem Fake vertrauen darf. Unsere schärfste Lektion. Drei separate Fehler überlebten 1.500 bestandene Tests, weil die Fakes freundlicher als das Drahtformat waren: ein Fake-Transport, der ein Audit-Payload umhüllte, das der echte nicht umhüllte, ein In-Memory-Link, der nie blockierte, und Peers, die nur gegen sich selbst spielten. Interop kann nur gegen etwas getestet werden, das man nicht selbst geschrieben hat.

Wo die Frist hin gehört. Ein Peer, der ewig antwortet, darf uns nicht in einem entschiedenen Spiel festhalten, und ein Peer, der verstummt, darf nicht zu unserer technischen Niederlage werden. Jedes Warten ist begrenzt und jedes Ende wird angekündigt statt angenommen – siehe unten.

Unser eigenes Protokoll ratenbegrenzen. Der Gatekeeper existiert, um ein guter Bürger gegenüber Anthropic und Gmail zu sein. Ihn auf den Gegner anzuwenden hätte uns fast Spiele gekostet: Unser eigener Limiter hätte eine Antwort über ihre 30-Sekunden-Frist hinaus verzögern können.

Strategien und warum kein RL

Cop – eine skriptierte Halbierungs-Versiegelung (strategy/seal_cop.py), keine Zug-für-Zug-Bewertung: Die mittlere Spalte einmauern, während man die Spur daneben entlanggeht, das Tor nehmen, die Hälfte abschneiden, die den Thief hält, und der Thief endet in einer 3×3-Tasche mit uns. Von dort löst domain/endgame.py die Tasche exakt – Barriereplatzierung ist ein Zug in seinem Spielbaum – und bepreist jeden Sieg auf Ko-Lokation, weil eine Betretungs-Behauptung der einzige Fang ist, den jede Implementierung ehrt; ein Thief, der dort versiegelt ist, wo wir nie hingehen können, wird von unserer eigenen Bank als remote seal bewertet und als Plan abgelehnt. Das Ein-Barriere-Schloss (strategy/lock.py: unser Körper plus eine Wand, Adjazenz erforderlich) beendet einen gepinnten Thief früh. Die Bank (tests/regression/) treibt ihn gegen reagierende Gegner – einschließlich eines Thief, der die eigene Wandlinie des Skripts besetzt – und er konvertiert alle von ihnen innerhalb der 35 Züge, durch einfachen beanspruchbaren Fang (test_seal_converts_every_reacting_thief.py). Belief-geführte Verfolgung (cop_brain.py) bleibt der Fallback für ein Brett ohne Lokalisierung.

Thief – Überlebenshorizont-Flucht als Stapel harter Böden, nicht gierige Distanz- maximierung: nie einen Zug innerhalb eines Schritts vom Cop beenden, Zellen verweigern, die ein halbgebauter Zaun bereits billig zu versiegeln macht, ein erreichbares 4×4 behalten, auf der Cop-Seite eines sich bildenden Schnitts bleiben (ein Versiegeler darf keine Wand vervollständigen, die ihn von uns trennt), und in den Lücken eines im Bau befindlichen Zauns stehen – eine Präferenz, die nach den Sicherheitsböden läuft, weil sie, bevor sie lief, uns einmal in die Ecke marschierte, die ein Gegner dann zumauerte (test_ahk_yosi_corner_hunt.py spielt diese Todeslinie Zug um Zug ab). Er überlebt jede archivierte Gegnerlinie in matches/ und alle 24 Tie-Break-Varianten eines reagierenden Eckejagd-Cops.

Hinweis-Politik – Bluffen ist eine strategische Ressource mit Kosten: Eine Behauptung offenbart die Zelle des Behauptenden, also schenkt eine falsche Fangbehauptung dem Thief unsere genaue Position für nichts.

Kein Reinforcement Learning, bewusst:

  • Nur ~60 echte Spiele sind in der gesamten Liga verfügbar – bei weitem nicht genug, um eine Policy über einen so großen Zustandsraum zu lernen.

  • Das Gegnerverhalten ist nicht stationär; jedes Team liefert etwas anderes.

  • Nichtdeterminismus würde Replay brechen, und Replay ist ein benotetes Deliverable.

  • Die Heuristiken schlagen die Baseline bereits deutlich (siehe unten), also wäre RL Risiko ohne gemessenen Nutzen.

Stattdessen betreiben wir Online-Gegnermodellierung, die auf die ~210 Beobachtungen zugeschnitten ist, die eine Serie tatsächlich liefert – Hinweisglaubwürdigkeit, Bewegungsmuster, Barrierenreaktion.

Gemessen mit der echten Match-Maschinerie auf zurückgehaltenen Spielen – Seed 11, nie beim Tuning verwendet, 60 Spiele pro Paarung:

Paarung

Fänge

Rate

95 % Wilson-KI

unser Cop gegen gierigen Dieb

60/60

100 %

94–100 %

gieriger Cop gegen unseren Dieb

0/60

0 % (100 % Überleben)

0–6 %

gierig gegen gierig (Referenz)

4/60

6,7 %

2,6–16 %

Null Peer-Meinungsverschiedenheiten und null Audit-Fehler über alle 180 Spiele.

unsere Strategie vs. die gierige Baseline

Ein einziger einstellbarer Parameter entscheidet das Spiel, und es ist nicht der, den wir erwartet hatten:

welcher Regler das Spiel entscheidet

barrier_threshold bewegt die Fangrate über seinen Bereich von 4 % auf 100 %. Eine Barriere ist für beide Seiten unpassierbar, sodass ein Cop, der bei schwachen Hinweisen mauert, sich selbst von dem Dieb abschottet, den er verfolgt – wir haben wochenlang 0.15 ausgeliefert, was uns ungefähr ein Drittel unserer Spiele gekostet hat. lookahead ist ein echtes Null-Ergebnis: Die Tiefen 1–4 erzeugen byteidentische Spiele, weil ein isotroper Diffusionskern die Rangfolge der Kandidatenzüge bewahrt, die er eigentlich trennen soll.

Der Vorbehalt, den wir anzugeben verpflichtet sind. Unser Dieb überlebt jeden Cop, dem wir je begegnet sind oder den wir archiviert haben – und verliert trotzdem, etwa bei Schritt 30, gegen unseren eigenen Versiegeler, dessen Halbierungsplan kein Gegner gezeigt hat. Beide Richtungen sind festgehalten und nicht übertüncht (test_thief_beats_sealing_cops.py dokumentiert die Niederlage und ihren Preis; die Corner-Hunt- und Archiv-Suiten dokumentieren die Überlebensfälle), denn ein Dieb, der nur gegen die Cops bewertet wird, die er schlägt, ist gegen sich selbst bewertet worden.

Vollständige Herleitungen, Konfidenzintervalle, die Token-Kosten-Tabelle und die Referenzen finden sich in notebooks/analysis.ipynb. Reproduzieren mit:

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

Was uns das Testen gegen einen Fremden lehrte

Wir klonten den Kurs-Referenzsimulator und richteten ihn auf uns. Nichts funktionierte, in keiner Richtung. Die Referenz nennt das MCP-Tool-Argument message bei drei Tools und payload bei einem; wir sendeten payload an alle vier und akzeptierten nur payload. Jeder Zug und jeder Vorschlag wurde von der Argumentbindung abgelehnt, bevor auch nur ein einziges Byte Spiellogik lief – gegen jeden Agenten, der auf der Referenz aufbaut, und das ist der Großteil des Kurses.

Vier weitere Inkompatibilitäten folgten: ein erforderliches timestamp, das wir nie sendeten, ein claimed_cell-Feld, das ihr Parser kategorisch ablehnt, und drei Anspruchsfelder, deren Typen unterschiedlich waren. Dann stellte sich heraus, dass die Audit-Offenlegung als nackte Liste gesendet wurde, wo das Schema einen Umschlag vorsieht, sodass beide Peers TAMPERED für Spiele aufzeichneten, in denen niemand betrogen hatte.

Jeder dieser Punkte bestand unsere eigenen Tests. Der Commit-Reveal-Kern hingegen überstand den Kontakt unverändert: Unser commit_of reproduziert die Signatur der Referenz Byte für Byte.

Die Lektion, die wir aus jedem verteilten Projekt mitnehmen würden: Eine grüne Testsuite beweist, dass Ihr Code mit Ihren Annahmen übereinstimmt, nicht, dass Ihre Annahmen richtig sind.


Dokumentation

Dokument

Zweck

docs/PRD.md

Produktanforderungen (FR-* IDs, KPIs, Meilensteine)

docs/PLAN.md

Architektur: C4- und FSM-Diagramme, ADR-001..021, Modulkarte

docs/TODO.md

688-Aufgaben-Bauplan mit Rückverfolgbarkeit und Fortschritt

docs/HANDOFF-2026-08-14.md

Aktueller Stand, offene Punkte und jede gemessene Korrektur

docs/PROTOCOL.md

Was über die Leitung geht und was nie

docs/SECURITY.md

Bedrohungsmodell, Prompt-Injection-Abwehr, Geheimnisbehandlung

docs/UX.md

Nielsen-Heuristiken auf Dashboard-Entscheidungen abgebildet; Barrierefreiheit

docs/EXTENDING.md

Die vier Erweiterungsnähte, mit einem ausgearbeiteten Plugin

docs/CONFIG.md

Jeder Konfigurationsschlüssel, seine Datei und seine Verhandelbarkeit nach Anhang F

docs/CI.md

Was jedes Gate prüft und wie man einen Fehler reproduziert

CONTRIBUTING.md

Konventionen: Kern-Sync, Commits, Tests, Spieltags-Sperre

docs/ISO25010.md

ISO/IEC 25010-Qualitätsmerkmale auf Evidenz abgebildet

docs/edge-cases.md

Jede behandelte Randbedingung, jede mit Verweis auf ihren Test

docs/TOKEN_BUDGET.md

Gemessener Token-Verbrauch und das Kostenmodell

docs/OPEN_ITEMS.md

Was bekanntermaßen unvollständig ist, mit der Evidenz

notebooks/analysis.ipynb

Sensitivitätsstudien, Baselines, Kostentabelle, Referenzen

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

Mechanismus-Designs

docs/runbook-network.md

Tunnel- und Konnektivitätsverfahren

docs/research/

Quellen-Digests (Buch, Richtlinien, Referenzsimulator, A6-Retrospektive)


Audit eines Gegners

Commit-Reveal beweist, dass ein Peer die Geschichte nicht umgeschrieben hat. Es beweist nichts darüber, ob sie nach den Regeln gespielt haben, und das sind verschiedene Garantien – wir haben sie während der gesamten Ligaphase vermischt und konnten nach dem Verlieren einer Serie nicht sagen, ob das Spiel legal gewesen war.

Auditing ist konstruktionsbedingt nach dem Spiel: Die Regeln 33–35 machen ein Spiel bei widersprüchlichen Berichten ungültig, sodass ein Agent, der auf seine eigene Anschuldigung reagiert, einen Verdacht in eine gegenseitige Null verwandelt. Alles unten zeichnet Beweise auf und ändert nichts daran, wie wir spielen (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

Was eine Offenlegung klären kann und was nur die Leitung kann: Die versiegelte Aufzeichnung eines Peers enthält das, was dieser Peer versiegeln wollte. Bewegung und Barrieren sind allein aus einem Archiv überprüfbar; smell_grid, capture_claim, hint und Antwortzeiten sind nur gegen das überprüfbar, was auf der Leitung ankam, weshalb FrameLog sie so speichert, wie sie gesendet wurden. Das Audit meldet diese als nicht überprüfbar, anstatt sie in ein sauberes Urteil zu falten – „wir haben geschaut und zugestimmt“ und „es gab nichts zu sehen“ dürfen niemals gleich gelesen werden.

Drei Ergebnisse, die jede Strategie einschränken

Alle wurden während der Ligaphase durch Messung festgestellt, und alle sind tragend.

Eine Barriere kann das Spielfeld verkleinern, aber den Dieb nie fangen. Das Buch nennt drei Fangbedingungen (Regeln 46–47); die Kursreferenz implementiert genau eine. Ihre rules.py enthält thief_result und is_captured und nirgendwo eine Barrieren-Fang- oder Immobilisierungsprüfung. Jeder Gegner, dem wir begegnet sind, stammt von der Referenz ab, sodass eine Einkesselung ein Minispiel ergibt, das wir werten und sie nicht – der Widerspruch der Regeln 33–35. Jeder Fang muss ein Anspruch sein, den der Dieb bestätigt (PLAN ADR-020).

Ein einzelner Cop kann auf einem offenen Spielfeld nicht aufschließen. Ein 7×7-Gitter ist das kartesische Produkt zweier Pfade, also ist seine Cop-Zahl 2 (Maamoun & Meyniel 1987), und ein erschöpfender Fixpunkt über alle 49×49 Zustände findet keinen Zustand, von dem aus ein nur bewegungsfähiger Cop bei gleichzeitigen Zügen einen Fang erzwingen kann. Unser Cop, der einen Dieb bis auf Distanz 2 verfolgt und dort 28 Schritte hält, ist ein Theorem, kein Defekt. Barrieren sind die einzige Ressource, die die Antwort ändert (PLAN ADR-021).

Und mit einem Plan ändern Barrieren es tatsächlich. Die Halbierungsversiegelung verwandelt jeden reagierenden Dieb, den die Bank bauen kann – das Spielfeld halbiert, die Hälfte halbiert, das 3×3 exakt gelöst, endend in einem Kollokationsanspruch innerhalb der 35 Züge. Die beiden obigen Einschränkungen bestimmen weiterhin die Form dieses Sieges: Er muss mit einem Anspruch enden, den der Dieb bestätigt, und er kann nicht allein durch Bewegung erreicht werden. Was hier lange als unser größtes Wettbewerbsrisiko verzeichnet war – ein Cop, der bis Distanz 2 verfolgt und hält – ist geschlossen; das verbleibende Risiko ist ein Gegner, dessen eigener Cop einen Plan spielt, der so vollständig ist wie unserer, und die Untergrenzen des Diebs sind genau dagegen bepreist.

tests/regression/cop_duel.py ist der Cop-seitige Benchmark, mit dem diese Behauptungen getestet werden – unser Cop gegen einen adaptiven Dieb, mit dem Glauben, den der echte Ingress-Pfad aus Duft aufbaut. Eine aufgezeichnete Gegnerlinie reagiert nicht und kann das Schließen nicht messen.


Lizenz & Namensnennung

MIT (siehe LICENSE). Protokollformen und Artefaktschemata interoperieren mit dem Kurs-Referenzsimulator rmisegal/Game-P2P-Cop-Chase (Bildungslizenz); wo Buch und Code kollidieren, gilt das Buch.

Erstellt mit FastMCP, FastAPI, pydantic und 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