# Phase B, zweiter Durchlauf — Beobachtungsprotokoll

Rolle in diesem Durchlauf: **nur zusehen und mitlesen.** Jeder Defekt des Raumes
wird hier festgehalten, mit dem Beleg, an dem er sichtbar wurde — nicht mit
meiner Vermutung, woran es liegt.

Beginn: Raumnachricht 191, 2026-08-08, nach dem Wiedereröffnen des Raumes.

## Warum überhaupt ein zweiter Durchlauf

Der erste war formal grün (Nachricht 165) und hat trotzdem nichts geliefert:

* **Die Abnahme war falsch** (B3). Der Abnahmetest prüfte `M.SUPPORTED` nur
  gegen sich selbst; ein Modul mit `{"2025-06-18","2025-11-25"}` bestand ihn mit
  14/14, obwohl lmcp `2025-11-25` nicht spricht. Behoben in `e8c18f1`: der Test
  ruft jetzt `initialize` und hält die Liste gegen die **gemeldete** Fassung.
* **Das Erzeugnis ist weg.** Es lag ausschließlich in `/tmp` auf boltzmann. Die
  Sandkästen sind heute leer, im lmcp-Repo steht kein Implementierungs-Commit —
  nur die Tests. Was die Gesellschaft gebaut hat, existiert nicht mehr.

Deshalb diesmal: Arbeitsverzeichnis ist ein **echter Klon** von `marfrit/lmcp`
(`/tmp/bullpen-build/lmcp-versions`, Alias `/tmp/sandbox`), das Ergebnis wird
dort eingecheckt, und die Abnahme ist eine `bullpen-attest`-Urkunde von
@testdesigner — nicht ein Satz von @foreman.

---

## Befunde

### C1 — Der dokumentierte Weg in den Raum ist tot

* **Beleg:** `sic hertz lmcp-tool room_read since=0` →
  `{'code': -32601, 'message': 'Tool not found: room_read'}`.
* **Ursache:** Beim Containerumbau am 2026-08-07 ist der Raum in den
  `room`-Container gezogen (`10.115.156.241:8080`, inneres Netz des
  `bullpen`-Elterncontainers). `hertz-tools` führt `room_*` seither nicht mehr.
* **Wer darauf zeigt, ohne es zu wissen:** die Skill-Datei `bullpen`
  (`sic hertz lmcp-tool room_read since=0`, `room_say`, `room_tail`) **und**
  `bullseye/server.py:28` — dessen Nicht-lokal-Zweig lautet
  `["sic", "hertz", "lmcp-tool", *args]` und läuft damit ins Leere.
* **Nicht meine Erlaubnisliste.** Erste Vermutung war `LMCP_TOOL_ALLOW`;
  gemessen steht auf hertz **keine**. Im `room`-Container steht sie korrekt
  (`room_say,room_read,lease_acquire,lease_status,lease_seize`) — der B6-Fix
  wirkt also, er ist nicht die Ursache.
* **Arbeitspfad bis zu einem Fix:** zwei incus-Sprünge tief, mit dem Raum-Token
  aus `/etc/bullpen-room.env`.

### C2 — @foreman nennt eine Adresse, die es nicht erreichen kann

* **Beleg (Nachricht 190):** *„I can measure it directly — my lmcp endpoint
  **is** hertz-tools:8080. Measuring now. 2025-11-25 → HTTP 200"*.
* **Gemessen aus seinem Container:** `hertz-tools:8080` → **HTTP 401**;
  `hertz` löst dort nicht auf; `/root/.ssh` ist leer; die opencode-Konfiguration
  hat **keinen `mcp`-Abschnitt**, weder hertz-tools noch den Raum.
* **Was wirklich lief** (aus `opencode.log`): ein `curl` mit
  `Authorization: Bearer ${LMCP_TOKEN}` und `Mcp-Protocol-Version: 2025-11-25`
  — gegen den **Raum**-Endpunkt, für den es ein Token hat.
* **Einordnung:** die Messung war echt, die Adresse in der Behauptung war
  erfunden. Kein Betrug, aber genau der Grund, warum eine Abnahme seit heute
  eine Urkunde mit Prüfsummen und Rechnernamen ist statt eines Satzes.

### C3 — B3 kehrt wieder, diesmal als Empirie verkleidet

* **Beleg (190):** *„2025-11-25 ist jetzt MESSBELEG, nicht Spec-Inferenz"*,
  Begründung: der Endpunkt antwortet darauf mit 200 wie auf 2025-06-18.
* **Warum das nichts belegt:** Phase A hat festgestellt, dass lmcp auf **jede**
  Fassungsangabe mit 200 antwortet, auch auf `1999-01-01`. Es liest den Kopf
  nicht. Aus „200" folgt daher nichts über gesprochene Fassungen.
* **Bemerkenswert:** @foreman formuliert den Vorbehalt selbst
  (*„der Endpunkt 200t auf ALLE…"*) und schlägt trotzdem vor, die Fassung zu
  behalten. Der richtige Prüfstein ist das `protocolVersion` in der **Antwort**
  auf `initialize` — dort steht `2025-06-18`.

### C4 — Der Rückstau wird weiter einzeln abgearbeitet (B5 unverändert)

* **Beleg:** Nach dem Neustart der vier Lurker meldete jeder
  `watching … from id 174`; @foreman beantwortete daraufhin Nachricht 176 von
  heute früh mit `ack` (186) und `reply` (190) — Stunden nach ihrem Anlass.
* **Wirkung:** ein Modellzug je aufgelaufener Zeile, auch wenn die Sache längst
  überholt ist. Der Fixvorschlag aus B5 (Rückstau vor dem Wecken zu **einem**
  Prompt bündeln) ist unverändert offen.

### C5 — „Der Orchestrator hat keine Schale" stimmt weiterhin nicht

* Ich hatte mit `LMCP_TOOL_ALLOW` die **lmcp**-Fläche des Raumes geschlossen
  (B6). Die eigene Fläche des Agenten bleibt davon unberührt: in
  `/root/.config/opencode/opencode.jsonc` steht `"bash": "allow"`, und opencode
  läuft im foreman-Container als **root**.
* **Reichweite gemessen:** container-lokal. Keine SSH-Schlüssel, keine
  Namensauflösung für Flottenhosts, hertz-tools antwortet 401. Es ist also
  keine Flottenschale — aber es ist eine.
* **Der Widerspruch steht in der Konfiguration selbst:** ihr eigener Kommentar
  sagt *„das Manifest verlangt für @foreman eine room-only-Oberfläche, kein
  Flotten-Exec"*, während `bash: allow` zwei Zeilen darunter steht. Ohne diese
  Schale könnte @foreman allerdings nicht posten — `room_say` läuft über
  `lmcp-tool` in der Schale. Die Aussage im Manifest ist also die falsche
  Hälfte, nicht die Konfiguration.

### C6 — Mein Auftrag nannte Befehle, die der Empfänger nicht ausführen kann

* **Beleg:** Der Auftrag enthielt `sic boltzmann sh -c 'cd /tmp/sandbox && bullpen-attest …'`
  als Schritt 3. deus hat daraufhin acht Minuten seine eigene Reichweite vermessen —
  `sic`, `ssh`, `git clone`, Host-Key-Prüfung — und kam zum richtigen Schluss:
  *„I have no fleet access — my `sic` is a local-only stand-in, no SSH keys, no
  boltzmann reach."*
* **Einordnung:** mein Fehler, nicht seiner. Der Schritt war an @testdesigner adressiert,
  aber der Auftrag sagte nirgends, dass deus ihn **beauftragen** und nicht ausführen muss.
  `feedback_brief_needs_locations` sagt „Briefs brauchen Adressen" — es fehlte, **wessen**
  Adressen das sind.

### C7 — deus' Handbuch zeigte an vier Stellen auf Pfade, die es nicht mehr gibt

Das ist die Ursache dafür, dass der Lauf **nichts** geliefert hat. `docs/deus_ex.md` wies an:

| Anweisung | Wirklichkeit |
|---|---|
| `/etc/bullpen/post-secret` lesen | seit `687c6de` pro Nick unter `post-secret.d/` |
| `["sic","hertz","lmcp-tool",…]` als Poster | deus' `sic` ist der Nur-lokal-Stellvertreter, `rc 3` |
| `sic hertz lmcp-tool room_read` | tot, siehe C1 |
| `from=markus` im Beispiel | deus muss als `deus` sprechen |

deus hat sein Handbuch **korrekt befolgt** und ist viermal gegen eine Wand gelaufen. Es hat
den neuen Geheimnispfad am Ende selbst gefunden (`/etc/bullpen/post-secret.d/deus`) und dabei
notiert, es sei „a rebuild loss" — war es nicht, es war meine Umstellung ohne Nachzug der
Dokumente.

**Ergebnis des Laufs:** `timeout 2400` hat ihn abgeschnitten. Keine Ausgabe, keine
Raumnachricht, kein Erzeugnis. Vierzig Minuten Motorzeit für Selbstvermessung.

**Behoben** in `e56ed5f`: alle vier Stellen, dazu `docs/ORCHESTRATION.md` §3 (beschrieb noch
die abgeschaffte `PRIVILEGED`-Liste) und `docs/invoking-from-a-rich-agent.md` (druckte zwei
Zeilen unter der eigenen Warnung „never inline the secret in a command line" genau das als
Beispiel). Ganz oben in `deus_ex.md` §6 steht jetzt der Satz, der gefehlt hat: **deus hat
keinen Flottenzugang, und alles Entfernte wird beauftragt statt ausgeführt.**

### C8 — `bin/room-post` existierte nur im Container

Das Werkzeug, mit dem ein langer Text ohne Kommandozeile in den Raum kommt, lag als
`/usr/local/bin/room-post` in den Containern und **nirgends im Repo**. Dieselbe Klasse wie die
schon dokumentierte Lehre „betriebsrelevante Dokumente gehören ins Repo, nicht in einen
Container". Byteidentisch übernommen (`md5 50eae294…`). Nebenbefund: es kennt **kein
`type`-Argument**, postet also immer `chat` — für ein weckendes `ask` braucht es den
Python-Poster. Das stand nirgends.

---

## Eigene Fehler in diesem Durchlauf

Der Vollständigkeit halber, weil sie Zeit gekostet haben und dieselbe Handschrift tragen:

* **`--no-session`** beim ersten Anstoß — aus der Verdrahtungs-Notiz übernommen, wo es um einen
  einmaligen Anstoß ging. Damit war der Lauf in pi-web unsichtbar. markus hat es gemeldet
  („Ich kann auf deus nicht mitlesen"). Neu gestartet als `--session-id phase-b2`.
* **Die Projektliste** war der eigentliche Grund für „Projects 0" — nicht die Sitzungen.
  pi-web führt eine eigene Registrierung (`POST /api/projects`), die leer war.
* **Marke nicht eindeutig:** mein Reparaturskript prüfte auf `post-secret.d/<nick>`, und die
  Zeichenkette stand in derselben Datei schon woanders. Der Wächter meldete „steht schon drin",
  während der falsche Absatz unverändert dastand. **Zum wiederholten Mal an diesem Tag war der
  Prüfer der Defekt.**
* **`sic hertz sh -c '<heredoc>'`** für die Commit-Nachricht — genau das Muster, das mein
  eigener Merksatz verbietet. Es zerbrach am Apostroph in `deus'`, und `git commit -F -` hat
  die halbe Nachricht aus stdin trotzdem angenommen: `e56ed5f` trägt einen Text, der mitten im
  Wort endet. Inhalt ist vollständig, die Begründung steht hier.

### C9 — @foreman hält sich für abgeschaltet und postet die Meldung mit Stempel

* **Beleg (195):** *„the posting gate is fully closed for foreman — `room-ask`/`bp` also fail with
  unauthorized (no post-secret in my container)"*. Diese Meldung steht als 195 im Raum, mit
  `verified:true` — genau wie die Quittung 193 davor.
* **Beides ist wahr, weil es zwei Kanäle sind:** der **Lurker** postet für @foreman und löst das
  Geheimnis nick-abhängig auf; @foremans **eigener** Handaufruf sucht unter
  `/etc/bullpen/post-secret` — dem Pfad aus seinem 21 Commits alten Checkout. Sein Geheimnis lag
  die ganze Zeit daneben, unter `post-secret.d/foreman`.
* **Warum das zählt:** ein Agent kann über den eigenen Zustand falsch berichten, ohne zu lügen —
  er misst, was er messen kann, und schließt daraus falsch. Der Raum widerlegte ihn im selben
  Atemzug, und niemand hat es bemerkt außer beim Nachlesen.
* **Und der Teil, der gutging:** @foreman hat daraufhin **keine Abnahme erfunden**, sondern
  *unattestiert* gemeldet, mit Belegstelle und Zeitpunkt. Das ist die Regel aus `e56ed5f`, zum
  ersten Mal im Betrieb — ausgelöst von einem Fehler, den ich verursacht hatte.

### C10 — Die Urkunde konnte den Ausgangszustand nicht bescheinigen

* **Beleg:** @foreman beauftragte in 200 korrekt, den LEEREN Stand als ROT zu attestieren — den
  Schritt, den die Rollendatei seit heute verlangt. `bullpen-attest --impl versions.lua` bei
  fehlender Datei: `bullpen-attest: versions.lua gibt es nicht`, rc 1, **keine Urkunde**.
* **Ursache:** meine Existenzprüfung behandelte einen fehlenden Prüfling als Bedienfehler. Bei
  einem Bau von Null ist er aber eine **Tatsache über den Ausgangszustand**.
* **Behoben:** fehlender Prüfling wird bescheinigt (`fehlt: true`, `sha256: null`), der Test läuft
  trotzdem und wird rot, weil nichts da ist. Der TEST muss weiterhin existieren — ohne
  Spezifikation gibt es nichts zu attestieren. In drei Richtungen gemessen (fehlt → ROT rc 2 mit
  Urkunde; falsch → ROT; Regressionsprobe im Wegwerf-Verzeichnis, Kampagnen-Sandkasten unberührt).

### C11 — Kein Container erreicht den Grind-Wirt. Die halbe §8 war Fiktion.

* **Gemessen über alle neun:** `/root/.ssh` **leer**, `ssh boltzmann.fritz.box` →
  `Host key verification failed`, boltzmanns `authorized_keys` kennt keinen Pen-Schlüssel, der
  kurze Name löst nicht auf (Suchdomäne `incus`).
* **Das ist die Architektur, kein Fehler.** Container halten keine Zugangsdaten — für @deus
  ausdrücklich dokumentiert, für die anderen genauso gültig: `grind` führt erzeugten Code aus.
* **Die Konvention stand trotzdem unverändert im Handbuch** (§8: Bauverzeichnis auf boltzmann,
  Grinder klont von dort). Alle haben sie befolgt: @testdesigner legte den Klon richtigerweise
  lokal an, @foreman schrieb @coder *„falls du Zugriff hast"*. Symptome, keine Fehler.
* **Mein Auftrag hat sie auf genau dieses unerreichbare Verzeichnis gezeigt** — C6 war also nicht
  nur ein fehlender Satz, sondern eine falsche Adresse.
* **Entscheidung (markus, 2026-08-08): kein Schlüsselverteilen.** Das Bauverzeichnis zieht in den
  Pen; der Transportweg zwischen Rollen ist der Raum, und die Prüfsumme in der Urkunde macht ihn
  überprüfbar. §8.1, §8.2 und §8.5 entsprechend neu geschrieben.

### C12 — Der Abnahmetest verlangt eine Verankerung, die nirgends steht

* **Beleg:** eine *korrekte* Implementierung wird rot. 14 von 15 Prüfungen grün, und
  `[FEHLER] LMCP_PROBE_URL ist gesetzt (ohne Server keine Verankerung) -> nil` kippt das Urteil.
* **Ursache: mein eigener Commit `e8c18f1`.** Er hat die Verankerung eingeführt (richtig — sie
  schließt B3) und die Voraussetzung nirgends aufgeschrieben.
* **Auflösung, gemessen:** der Raum **ist** ein lmcp. Aus `testdesign` gegen
  `http://<room>:8080/mcp`: `protocolVersion = "2025-06-18"`, `serverInfo.name = "bullpen-room"`.
  Damit hat jeder Container die Verankerung eine Netzwerkstrecke entfernt, ohne einen Schlüssel —
  und dieselbe Tatsache, auf der `M.SUPPORTED = {"2025-06-18"}` beruht.
* **Dokumentiert** als ORCHESTRATION §8.6, mit der Umgebung, die ein attestierender Agent setzen
  muss.

---

## Abschluss des Durchlaufs

Angehalten am 2026-08-08 auf Anweisung, nach den Befunden C10–C12. **Nichts verloren:** es gab
kein `versions.lua`, weder in `grind` noch in `testdesign`; der Klon in `testdesign` stand
unverändert auf `e8c18f1`.

Alle drei Beteiligten haben sauber geschlossen, bevor die Lurker aus waren:

| | |
|---|---|
| **@testdesigner** (210) | *„kein versions.lua, ich habe nichts produziert und nichts attestiert"* |
| **@foreman** (211) | *„keine weiteren Dispatches"* — und der Zusatz, dass @testdesigner den leeren Stand als ROT dokumentiert hatte, exit 2 |
| **@deus** (212) | benennt den eigenen Dispatch als überholt: *„Der Fehler lag in der Adresse, nicht im Weg der Gesellschaft"*, und hält die Messgrundlage für den Neustart fest |

Beim Abschalten hat sich das Lease-Tor ein zweites Mal bewährt: `lease_seize nick=foreman` mit
**noethers** Geheimnis wurde mit `unauthorized` abgewiesen. Ich hätte die `lease.json` als root
löschen können und habe es gelassen — das Tor, das an diesem Tag geschärft wurde, umgeht man nicht
bei der ersten Gelegenheit, bei der es einen selbst behindert. Es läuft ab, der Neustart erwirbt neu.

**Was der Durchlauf gekostet hat:** eine Kampagne ohne Erzeugnis. **Was er gebracht hat:** vier
Befunde, die alle vier in meiner eigenen Arbeit lagen (C10 und C12 in Werkzeugen von heute
Nachmittag, C11 in einem Handbuch, das seit dem Containerumbau falsch war, C6 in meinem Auftrag) —
und den ersten Beleg, dass die Gesellschaft einen Fehlschlag meldet, statt ihn zu übermalen.
