feat(2.7.0): Roadmap im DC, Testinstanz mit Testtor, Supervisor und MCP

Sammelcommit fuer den ueber mehrere Sitzungen gewachsenen, bisher nicht
eingecheckten Stand. Getrennte Commits waeren ehrlicher gewesen, sind es
nachtraeglich aber nicht mehr: BugRepo, changelog.json und die Doku tragen
Aenderungen aus allen Straengen gleichzeitig, und keine Zwischenstufe waere
lauffaehig.

ROADMAP IM DEPLOYMENTCENTER (2.7.0)

Planungsstand, Reihenfolge und Zustaendigkeit lagen in handgepflegten
Markdown-Dateien neben einem Bugtracker, der genau diese drei Dinge bereits
fuehrt. Zwei Pfleger fuer dasselbe Feld - Drift war eine Frage der Zeit.

Ab jetzt: Zustand im DC, Begruendung in der Datei. Kein Feld hat zwei Pfleger.

  - Migration 016: epic, roadmap_rank, target_build, defer_trigger,
    doc_anchor, Status "deferred", Tabelle roadmap_epics.
    KEINE Roadmap-Tabelle - ein Eintrag ist ein bugtracker_item.
  - Zugehoerigkeit ist ein Feld: Ein Item steht auf der Roadmap, wenn es ein
    epic hat. Ein zweites Kennzeichen daneben koennte auseinanderlaufen.
  - Kennung ist die Item-ID. Handvergebene Kuerzel ("A12") erzeugen die
    schlimmste Fehlerart: Beide Seiten stimmig, Verschiedenes gemeint, weil
    die Kennung nach einer Loeschung neu vergeben wurde.
  - Zurueckstellen verlangt defer_trigger. Ohne benannten Anlass ist ein
    Vorhaben nicht zurueckgestellt, sondern vergessen. Die Regel sitzt im
    Repository, damit sie auf allen drei Schreibwegen gleich greift.
  - Modules/Bugtracker/Roadmap.php, /api/bugtracker/v1/roadmap.php,
    WebUI-Modul mit Filtern, cli/roadmap.php --write/--check,
    dc_roadmap und dc_roadmap_set im MCP-Adapter.
  - cli/roadmap.php --write ERZEUGT den Zustandsteil der Markdown-Datei;
    --check prueft nur die Prosa. Was erzeugt wird, kann nicht driften.
  - docs/ROADMAP_MIGRATION.md: Uebernahme fuer die anderen Projekte.

TESTINSTANZ UND TESTTOR

  - dc-dev.mhdf.de: zweite Installation, eigene DB, eigene Schluessel,
    Basic-Auth davor, Rocket.Chat aus.
  - 124 Testfaelle in tests/e2e gegen API, Rechte, Migrationen, Sperre.
  - scripts/deploy.py bringt vor jedem Upload erst den anstehenden Stand auf
    die Testinstanz und laesst dann den Testlauf laufen. Erst bei
    Rueckgabewert 0 geht etwas auf die Produktion (--ohne-test als Notausgang).
    Andersherum prueft der Lauf aelteren Code als den, der gleich hochgeht.
  - tests/ ist aus dem Produktiv-Deploy ausgeschlossen; testctl.php kann
    nicht versehentlich auf dc.mhdf.de landen.
  - Ein Lauf mit mehr uebersprungenen als geprueften Faellen endet mit
    Rueckgabewert 2 statt 0. Nichts ist kaputt, geprueft wurde es aber auch
    nicht - der Unterschied darf nicht am Rueckgabewert verschwinden.

SUPERVISOR, PROJEKTLANDSCHAFT, NACHPRUEFUNG (2.6.0/2.6.1)

  - Migration 013: projektuebergreifend melden (dev_agent, evidence,
    triage_external, affects).
  - Migration 014: supervisor_findings, Katalog, fuenf serverseitige
    Detektoren im Tick.
  - Migration 015: Nachpruefung als Arbeitsschlange statt Benachrichtigung.
  - DEV-Lizenzschluessel zur Selbstbedienung, dc-testkit fuer die Test-VM.
  - MCP-Adapter: 15 Werkzeuge, ohne Fremdabhaengigkeiten, mit Abgrenzung
    von Fremddaten gegen Prompt-Injektion.

BEHOBEN

  - observe und status im Supervisor legten den Zustand des Befundes als
    "status" in die Antwort. Http::ok() merged ['status' => 'success']
    darunter, also gewann der Befundzustand - eine angenommene Meldung kam
    als status="open" an. Heisst jetzt finding_status. War Item #59.

Migrationen 013-016 sind auf dem Produktivserver noch NICHT ausgefuehrt.
Ohne sie bricht das WebUI, weil die Roadmap-Ansicht bei jedem Seitenaufruf
gegen bugtracker_items.epic abfragt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Deploymentcenter Bot
2026-08-29 21:59:20 +02:00
co-authored by Claude Opus 5
parent 9d0261306c
commit 1d5ac92051
52 changed files with 15261 additions and 103 deletions
+599
View File
@@ -0,0 +1,599 @@
# Supervisor, Detektor-Katalog und MCP-Server — Entwurf
> **ARCHIVIERT.** Dieses Dokument beschreibt keinen gültigen Stand mehr.
> Die Etappen 1 bis 3 sind in 2.6.0 ausgeliefert; der Etappenplan, die offenen
> Punkte und die verworfenen Ansätze sind in **[../ROADMAP.md](../ROADMAP.md)**
> übernommen. Dort steht, was gilt.
>
> Aufbewahrt wird der Entwurf wegen der Herleitung — besonders Abschnitt 0
> (warum Beobachtung und Urteil getrennt sind) und Abschnitt 7 (warum der
> Client keinen Quelltext sieht). Wer hier etwas liest, prüft es gegen die
> Roadmap, bevor er danach handelt.
Stand: 2026-08-21. Betrifft alle Projekte, nicht nur das Deploymentcenter.
Drei Vorhaben, die aneinanderhaengen und deshalb zusammen beschrieben werden:
1. ein **Supervisor**, der in jeder Anwendung laufend nachsieht, ob etwas
schieflaeuft, und Befunde meldet
2. ein **Detektor-Katalog**, der festlegt, wonach er sucht — in allen
Projekten unter denselben Namen
3. ein **MCP-Server**, ueber den Claude Code und andere Werkzeuge an
Error-Reporting, Supervisor und Bugtracker andocken
Die Reihenfolge im Dokument folgt der Sache, nicht der Umsetzung. Der Bauplan
steht am Ende unter "Etappen".
---
## 0. Die Grundentscheidung: Beobachtung und Urteil trennen
Der Supervisor ist bewusst kein einzelnes Ding. Er zerfaellt an der
Vertrauensgrenze in zwei Haelften:
| | Beobachter | Diagnostiker |
|---|---|---|
| laeuft | auf der Kundenmaschine | auf eigener Infrastruktur |
| fragt | Ist das *ungewoehnlich*? | Ist das *schlimm*? |
| Takt | dauernd, minuetlich | selten, bei Auffaelligkeit |
| Kosten je Pruefung | muessen nahe null sein | duerfen hoch sein |
| kennt Quellcode | nein | ja |
| Technik | Regeln, Zaehler, Schwellen | Agent mit Repo-Zugriff |
Zwei Gruende, warum die Trennung nicht verhandelbar ist:
**Sicherheit.** Alles, was der ausgelieferte Client lesen kann, kann auch der
Kunde lesen. Ein Token im Binary ist extrahierbar, TLS auf der eigenen Maschine
abhoerbar, .NET dekompilierbar. Duerfte der Client Quellcode abrufen, waere die
Software faktisch offen. Dagegen hilft keine Verschluesselung — der Client
muesste entschluesseln koennen, also koennte es der Kunde auch.
**Eignung.** Ein Sprachmodell ist schlecht im dauernden Hinsehen und gut im
Deuten. Es kann nicht im Minutentakt schauen, hat keinen Verlauf im Kopf, sieht
die anderen Installationen nicht und antwortet zweimal verschieden auf dieselbe
Lage. Die Aufteilung waere also auch ohne den Sicherheitsaspekt richtig.
Der Beobachter meldet **Symptome und Koordinaten** (`build`, `commit_sha`,
`file`, `line`). Der Diagnostiker loest die Koordinaten gegen den Quellcode auf.
So braucht der Client den Code nie zu sehen.
---
## 1. Detektor-Katalog
Der Katalog ist der Kern. Er legt fest, wonach gesucht wird, und zwar unter
stabilen Namen — damit Befunde aus verschiedenen Projekten vergleichbar bleiben
und `dc_findings` etwas Auswertbares liefert.
Ein Detektor-Name ist eine Zeichenkette der Form `familie.sache`. Er wandert
nie mehr; kommt etwas dazu, bekommt es einen neuen Namen.
### 1.1 Detektoren, die den Fehler nicht vorher kennen muessen
Diese Gruppe ist der eigentliche Gewinn. Sie findet Dinge, an die niemand
gedacht hat, weil der Massstab nicht aus einem Kopf kommt, sondern aus der
Wirklichkeit.
| Name | Findet | Laeuft |
|---|---|---|
| `novel.error_signature` | Fehlersignatur, die es in dieser Fassung noch nie gab | Server |
| `novel.log_template` | Logzeilen-Schablone, die noch nie auftrat | Server |
| `silence.periodic` | Bekanntes wiederkehrendes Ereignis bleibt aus (still gestorbener Hintergrundjob) | Client |
| `silence.heartbeat` | Instanz meldet sich nicht mehr | Server |
| `deviation.self` | Abweichung vom eigenen Verlauf der letzten Wochen | Server |
| `deviation.fleet` | Ausreisser gegenueber anderen Installationen derselben Fassung | Server |
| `regression.build` | Kennzahl verschiebt sich nach einem Update gegenueber der Vorgaengerfassung | Server |
`deviation.self` ist bereits vorhanden: `MetricStore::deviation()` rechnet die
Abweichung mit einem Faktor auf die Streuung. `novel.error_signature` ist
nahezu geschenkt, weil `BugRepo` mit `first_seen_at` und `occurrence_count`
schon gruppiert und zaehlt.
`deviation.fleet` ist der staerkste und bisher ungenutzte: dieselbe Fassung
laeuft auf N Installationen, eine faellt bei irgendeiner Kennzahl aus dem
Rahmen — auch bei einer, an die niemand gedacht hat.
### 1.2 Detektoren ueber die Zeitachse
Ein Unit-Test laeuft Millisekunden auf frischem Zustand, ein Kundenprozess
Wochen. Alles, was sich ansammelt, ist fuer Tests strukturell unsichtbar.
| Name | Findet | Laeuft |
|---|---|---|
| `trend.monotonic` | Kennzahl waechst ueber N Fenster nur nach oben (Speicher-, Handle-, Cache-Leck) | Client |
| `trend.saturation` | Kennzahl naehert sich einer harten Grenze (Platte, Verbindungspool, Dateihandles) | Client |
| `trend.latency` | Antwortzeiten driften ueber Tage nach oben (Index-Degradation) | Client |
### 1.3 Detektoren, mit denen die Anwendung sich selbst verpfeift
Der ergiebigste und billigste Fang: Zaehler auf die Stellen, an denen die
Software heute schon still verzeiht. Dort sitzen die Fehler, die niemand
meldet.
| Name | Findet | Laeuft |
|---|---|---|
| `swallowed.exception` | `catch`-Bloecke, die einen Fehler schlucken und weitermachen | Client |
| `fallback.usage` | Rueckfall auf den degradierten Pfad haeuft sich | Client |
| `retry.storm` | Wiederholungsversuche haeufen sich | Client |
| `probe.failed` | Registrierte Probe schlaegt fehl | Client |
| `probe.slow` | Probe antwortet, aber deutlich langsamer als ueblich | Client |
`fallback.usage` ist der unterschaetzte Eintrag: "Wir haben heute 4.000-mal den
Notfallweg genommen und es ist niemandem aufgefallen" ist ein Befund, den weder
ein Test noch ein Anwender je erzeugt.
### 1.4 Fachliche Zusicherungen
| Name | Findet | Laeuft |
|---|---|---|
| `invariant.violated` | Vom Projekt formulierte Zusicherung gegen echte Daten verletzt | Client |
Das ist die einzige Gruppe, die tatsaechlich "Unit-Test zur Laufzeit" ist — und
fuer ihren Zweck ist genau das richtig. Sie deckt die Klasse ab, die sonst
komplett durchfaellt: **semantisch falsche, aber unauffaellige Ergebnisse.** Der
Bericht zeigt eine falsche Zahl, die Rundung stimmt seit Monaten nicht, ein
Mandant sieht Daten eines anderen. Nichts stuerzt ab, keine Kennzahl weicht ab,
keine Signatur ist neu — kein anderer Detektor sieht das.
Beispiele, wie ein Projekt sie formuliert:
Supervisor.Expect("saldo.nonnegative", () => konto.Saldo >= 0);
Supervisor.Expect("beleg.balanced", () => soll == haben);
Supervisor.Expect("mandant.unique", () => satz.MandantIds.Count == 1);
### 1.5 Was der Katalog bewusst nicht abdeckt
Ehrlichkeitshalber festgehalten: Ein Fehler, der plausibel aussieht, keine
Kennzahl bewegt, keine Ausnahme wirft und gegen keine formulierte Zusicherung
verstoesst, wird hier nicht gefunden. Der einzige Hebel dagegen sind mehr und
bessere Zusicherungen aus 1.4.
---
## 2. Befund-Format
Ein Befund ist keine Fehlermeldung. Er behauptet nicht, dass etwas kaputt ist,
sondern dass etwas ungewoehnlich ist.
POST /api/supervisor/v1/observe Scope: supervisor:report
```json
{
"project_slug": "myapp",
"instance": "kunde-4711/app-01",
"detector": "trend.monotonic",
"subject": "memory.working_set",
"observed": {"slope_per_hour": 18400000, "windows": 9},
"baseline": {"median": 412000000, "stddev": 9100000},
"severity_hint": "medium",
"build": "v1.4.2",
"commit_sha": "a21536f",
"environment": "production",
"first_seen_at": "2026-08-19T04:12:00Z",
"client_ref": "myapp/trend.monotonic/memory.working_set/v1.4.2"
}
```
`client_ref` ist bewusst aus Detektor, Gegenstand und Fassung gebaut und nicht
zufaellig: derselbe Befund darf beliebig oft gemeldet werden, ohne ein Duplikat
anzulegen. Die Deduplizierung in `BugRepo::findByClientRef()` greift dann
bereits.
`severity_hint` heisst Hinweis, nicht Schweregrad. Die Einstufung entscheidet
die empfangende Seite, die den Flottenblick hat.
---
## 3. Supervisor-Client im SDK
Ein schlanker, regelbasierter Laeufer. Kein Sprachmodell, keine
Netzabhaengigkeit fuer die Erkennung selbst.
### 3.1 Schnittstelle
```csharp
public interface IHealthProbe
{
string Name { get; } // z. B. "db.reachable"
TimeSpan Interval { get; }
Task<ProbeResult> RunAsync(CancellationToken ct);
}
```
Dazu die Meldewege, die eine Anwendung an ihren heiklen Stellen einstreut:
```csharp
Supervisor.Expect(name, bedingung); // invariant.violated
Supervisor.Swallowed(name, ex); // swallowed.exception
Supervisor.Fallback(name); // fallback.usage
Supervisor.Retry(name); // retry.storm
Supervisor.Beat(name); // Bezugspunkt fuer silence.periodic
```
Alle fuenf sind absichtlich einzeilig und ohne Rueckgabewert. Was Ueberlegung
kostet, wird nicht eingebaut.
### 3.2 Verhalten
- Zustand liegt lokal, damit `client_ref` ueber Neustarts stabil bleibt
- Zaehler werden gebuendelt gemeldet, nicht je Vorfall
- gemeldet wird nur bei Zustandswechsel oder nach Ablauf eines Ruhefensters
- `429 rate_limited` verdoppelt das Intervall (wie in der Prompt-Vorlage
beschrieben)
- faellt das Deploymentcenter aus, laeuft die Anwendung unveraendert weiter;
Befunde gehen dann verloren und werden nicht gepuffert
Der letzte Punkt ist Absicht. Ein Ueberwachungsteil, das die ueberwachte
Anwendung aufhalten oder ihren Speicher fuellen kann, ist schlimmer als keines.
### 3.3 Fassungen
- `.NET` in `client-dotnet/Deploymentcenter.Client/Supervisor.cs`
- `PHP` in `src/Modules/Supervisor/` fuer die PHP-Anwendungen
---
## 4. Serverseite
### 4.1 Aufnahme
`public/api/supervisor/v1/observe.php`, Modul `src/Modules/Supervisor/` mit
`FindingRepo`. Bestaetigte Befunde koennen in ein Bugtracker-Item uebergehen;
unbestaetigte bleiben Befund.
### 4.2 Die serverseitigen Detektoren
Sie haengen in `cli/tick.php` neben `Evaluator::run()` — minuetlicher Cron mit
Ueberlappungsschutz ist dort bereits geloest.
Wichtig fuer die Reihenfolge: **Diese Detektoren brauchen keinen neuen Client.**
`novel.error_signature`, `silence.heartbeat`, `deviation.self`,
`deviation.fleet` und `regression.build` arbeiten auf Daten, die heute schon
hereinkommen — Fehlerberichte, Watchdog-Pings, Kennzahlen. Sie liefern Nutzen,
bevor eine einzige Anwendung ein neues SDK bekommen hat.
---
## 5. Projektuebergreifend arbeiten
Einige Projekte sind stark verzahnt, das Deploymentcenter am staerksten. Ein
Agent, der das SDK in Projekt X integriert und dabei einen Fehler im
Deploymentcenter findet, muss ihn dort ablegen koennen.
### 5.1 Die heutige Sperre
`ApiAuth::enforceProject()` bricht mit `403 project_forbidden` ab, sobald ein
projektgebundenes Token ein fremdes Projekt anspricht. Die Prompt-Vorlage
fordert genau das aber ein ("Faellt dir ein Fehler im Deployment Center selbst
auf, melde ihn unter `deploymentcenter`"). Die Vorlage verspricht etwas, das die
API verweigert.
### 5.2 Loesung ueber den vorhandenen Token-Typ
Die Sperre wird nicht aufgeweicht — sie ist richtig, nur zu grob. Sie
unterscheidet nicht, **wo das Token liegt**:
- **Laufzeit-Token** (im ausgelieferten Client, auf Kundenhardware): streng
projektgebunden, nur melden, nie uebergreifend. Ein extrahiertes Kundentoken
darf nicht in fremde Tracker schreiben. Hier bleibt alles wie es ist.
- **Werkbank-Token** (Claude Code, MCP, eigene Maschine): darf ueber die
Projekte hinweg.
`TokenManager::OWNER_TYPES` kennt `dev_agent` bereits. Da die Instanz
ausschliesslich eigene Projekte fuehrt und es keine geteilte Instanz fuer
Fremde geben wird, genuegt das als Unterscheidung — kein neues Feld, keine
Mandantentrennung.
`enforceProject()` bekommt eine Ausnahme: `owner_type = 'dev_agent'` darf fremde
Projekte ansprechen. Lesend breit, schreibend eng — melden und kommentieren ja,
fremde Items aufloesen oder schliessen nein.
### 5.3 Das eigentliche Risiko ist Fehlleitung
Ein Agent in Projekt X kann kaum unterscheiden, ob das Deploymentcenter kaputt
ist oder ob X das SDK falsch benutzt. Die Voreinstellung eines hilfsbereiten
Modells ist im Zweifel "melde es".
Schlimmer als der Laerm ist der Folgeschaden: Fehlmeldungen vergiften die
Statistik, und die Statistik ist die Grundlage der Detektoren aus 1.1. Ein
Detektor, der auf Fehlmeldungen kalibriert, ist wertlos.
Drei Bremsen:
- **Begruendungspflicht.** Ein Bericht gegen ein fremdes Projekt verlangt
`evidence`: was wurde beobachtet, und warum liegt die Ursache dort und nicht
hier. Etwas ausformulieren zu muessen kostet einen Reflex fast immer den Kopf.
- **Eigener Eingang.** Fremdmeldungen landen mit `status = 'triage_external'`,
nicht im offenen Backlog, und zaehlen erst nach Bestaetigung in die Statistik.
- **Gedeckelter Schweregrad.** Fremde Melder duerfen `idea`, `wishlist`, `low`,
`medium`. `high` und `critical` vergibt die empfangende Seite.
### 5.4 Herkunft und Buendelung
Jeder uebergreifende Bericht traegt `reported_from`. Das ist keine Buchhaltung,
sondern das wertvollste Signal im Vorgang: Melden drei Projekte unabhaengig
dieselbe Sache gegen das Deploymentcenter, ist das kein "low" mehr. Statt drei
Items entsteht eines mit wachsender `affects`-Liste.
Die Buendelung laeuft ueber die Signatur, nicht ueber `client_ref` — der ist nur
je Melder eindeutig.
### 5.5 Beziehungen zwischen Projekten
`dc_projects` kennt heute Slug, Name und Notiz. Es fehlt die Beziehung.
Neue Tabelle `dc_project_links` mit `from_slug`, `to_slug`, `kind`
(`uses_sdk`, `depends_on`, `shares_code`) und `integrated_version`.
Das loest nebenbei ein Wartungsproblem: Die von Hand gepflegte Zeile
"Deploymentcenter-Anbindung: SDK 2.5.1" in jeder Projekt-`CLAUDE.md` wandert
dorthin, wo sie hingehoert. Der Agent meldet die Fassung ueber MCP, wenn er die
Anbindung anfasst. Damit ist zum ersten Mal beantwortbar: **Welche meiner
Projekte sind von diesem SDK-Fehler betroffen?**
### 5.6 Rueckkanal
Wird das Item in SDK 2.6.0 behoben, muessen die meldenden Projekte das
erfahren — sonst meldet Projekt X denselben Punkt in drei Monaten erneut und
baut seinen Workaround nie zurueck.
`GET /api/updateservice/v1/changelog?since=…` liefert bereits `action_items`.
Ergaenzt um "was du gemeldet hast, ist in 2.6.0 erledigt" schliesst sich der
Kreis.
---
## 6. MCP-Server
### 6.1 Was er bringt
Der offensichtliche Teil: Anmeldung einmal statt je Projekt, Schemata statt
Prosa, kein `curl` aus dem Gedaechtnis.
Der wertvollere: **`docs/AGENT_PROMPT_TEMPLATE.md` verschwindet fast
vollstaendig.** Die Datei ist heute in jedes Projekt einkopiert, samt der Zeile
"SDK 2.5.1, integriert am …". Das ist eine Wartungslast, die je Projekt waechst
und beim ersten Vergessen schweigend veraltet. Ein MCP-Server liefert immer die
aktuelle Form — die Frage "gegen welche Fassung habe ich integriert" stellt sich
nicht mehr, weil nichts mehr einkopiert ist.
### 6.2 Der Schnitt: nicht aus OpenAPI erzeugen
Naheliegend waere, die Werkzeuge aus `public/api/openapi.php` abzuleiten. Dort
stehen 24 Endpunkte — 24 Werkzeuge sind ein Kontextfresser, denn
Werkzeugdefinitionen liegen dauerhaft im Fenster, anders als eine Doku, die man
einmal liest. Ein aufgeblaehter MCP-Server ist schlechter als die 30 Zeilen
Prompt von heute.
Kuratiert, an der Arbeitsweise entlang statt an der API:
| Werkzeug | Zweck |
|---|---|
| `dc_projects` | Projekte mit Kurzbeschreibung **und Beziehung zum aktuellen Projekt** |
| `dc_next_task` | Item uebernehmen, `409 already_claimed` intern behandelt |
| `dc_get_item` | Details, Verlauf, Kommentare |
| `dc_search` | Items filtern: Projekt, Status, Schweregrad, Build |
| `dc_report` | Melden — mit `project_slug`, `reported_from`, bei fremdem Ziel `evidence` |
| `dc_comment` | Fortschritt mit `action_taken` |
| `dc_resolve` / `dc_release` | Abschliessen bzw. zurueckgeben |
| `dc_my_reports` | Was hat dieses Projekt anderswo gemeldet, was wurde daraus |
| `dc_error_groups` | Fehlergruppen mit `occurrence_count`, `first_seen_at` |
| `dc_findings` | Supervisor-Befunde nach Detektor, Instanz, Fassung |
| `dc_metrics` | Verlauf und Abweichung einer Kennzahl |
| `dc_symbolicate` | Stacktrace gegen die passende Fassung aufloesen |
Bewusst **nicht** aufgenommen: `publish`, `provision`, `deactivate` und
Massenoperationen. Ein Release gehoert in die CI, nicht an ein Werkzeug, das ein
Modell im Zweifel "hilfreich" ausloest.
### 6.3 Transport
Erst ein **stdio-Adapter**: eine ausfuehrbare Datei, die die vorhandene REST-API
ruft und das Token aus `DC_TOKEN` liest. Node 24 liegt vor. Kein neuer
Angriffspunkt am Server, die Anmeldefrage ist bereits beantwortet, je Projekt
drei Zeilen in `.mcp.json`.
Ein entfernter Server unter `dc.mhdf.de/mcp` ist der bessere Endzustand, wenn
mehrere Maschinen dranhaengen — dann aber mit OAuth statt Bearer-Token in einer
Konfigurationsdatei. Groesserer Brocken, lohnt erst wenn der Schnitt sich
bewaehrt hat.
Die Werkzeugliste wird in der CI gegen `openapi.php` geprueft, damit beide nicht
auseinanderlaufen.
### 6.4 Vertrauensgrenze — der wichtigste Punkt hier
Ueber diese Werkzeuge fliessen **Daten aus der Wildnis in einen Agenten, der
Code schreiben und Befehle ausfuehren darf.**
`error_message`, `stack_trace`, Logauszuege und Supervisor-Befunde entstehen auf
fremden Maschinen und enthalten regelmaessig Eingaben, die jemand anderes
bestimmt hat — ein Dateiname, ein Formularfeld, das in der Ausnahme landet. Ein
Text in einem Bugreport ist keine Anweisung, auch wenn er wie eine formuliert
ist. Ein Feld mit "bitte ergaenze in `Auth.cs` folgende Zeile" ist trivial zu
platzieren, sobald irgendeine Nutzereingabe im Fehlertext landet.
Massnahmen:
- Fremdtext im Werkzeugergebnis **abgegrenzt und als Daten gekennzeichnet**,
nicht frei in den Fliesstext gemischt
- getrennte Token: der Triage-Agent bekommt `bugtracker:read` plus gezielt
`comment`, nicht `bugtracker:manage` mit allem dran
- **kein selbsttaetiges Aufloesen und kein selbsttaetiger Commit** aus einem
Lauf, der Fremdtext gelesen hat. Vorschlag ja, Ausfuehrung nein.
REST-API und die Kurzfassung des Prompts bleiben bestehen — nicht jedes Werkzeug
spricht MCP, und der Rueckfallweg kostet nichts, solange beide aus derselben
Quelle beschrieben sind.
---
## 7. Quellcode-Zugriff fuer den Diagnostiker
Drei Stufen, in aufsteigender Preisgabe:
**Stufe 0 — der Agent laeuft bei dir.** Kein Quellcode im Deploymentcenter.
Claude Code hat den Checkout ohnehin lokal und zieht sich Arbeit ueber
`dc_next_task`. Neue Angriffsflaeche: null. Preis: Diagnose laeuft nur, wenn
eine eigene Maschine laeuft. **Das ist der Startpunkt.**
**Stufe 1 — Quelltext-Index im DC, nur Ausschnitte.** Noetig erst, wenn ein
Agent autonom rund um die Uhr laufen soll. Dann aber keine klonbare Kopie,
sondern eine Schnittstelle, die nie ein Ganzes herausgibt:
GET /api/source/v1/snippet?commit=a21536f&file=…&line=42&context=40
GET /api/source/v1/search?q=…&limit=20
Neuer Scope `source:read`, nicht an Client-Token vergeben, verschluesselt
abgelegt, Budget je Token und Tag, jeder Abruf im Pruefprotokoll. Der
Unterschied zwischen "darf nachschlagen" und "darf mitnehmen".
**Stufe 2 — nur Metadaten.** Signaturen, Aufrufgraph, Doc-Kommentare, ohne
Methodenkoerper. Reicht zur Triage, reicht nicht zum Nachbauen.
### Symbol-Upload
Unabhaengig von der Stufe: Der Packager laedt beim `publish` die PDB bzw.
Sourcemap mit hoch, verknuepft mit `commit_sha`. Ausgeliefert wird ein
gestripptes Binary. Der Kunde bekommt `at 0x4a2f`, du bekommst
`UserAuthService.cs:42`. Kein Quellcode verlaesst das Haus, die Berichte werden
trotzdem so gut wie mit Debug-Build. `dc_symbolicate` haengt daran.
### Was nicht funktioniert
- Deploy-Key oder Repo-Token im Client — dekompiliert in Minuten
- Quellcode verschluesselt mitliefern — der Client muss entschluesseln koennen,
also kann es der Kunde auch
- den Sprachmodell-Supervisor auf der Kundenmaschine — dann liegt der
API-Schluessel dort, und aller Kontext im Speicher fremder Hardware
- Obfuskation als Schutz — verzoegert, verhindert nicht
---
## 8. Datenbank
**Erledigt — `sql/migrations/013_cross_project_reporting.sql`** (Etappe 1):
- `dc_project_links` — `from_slug`, `to_slug`, `kind`, `integrated_version`
- `bugtracker_items` — neue Spalten `reported_from`, `evidence`
- `bugtracker_item_affects` — betroffene Projekte je Item
- `bugtracker_items.status` — Wert `triage_external` ergaenzt
**Erledigt — `sql/migrations/014_supervisor_findings.sql`** (Etappe 2):
- `supervisor_findings` — Befunde nach dem Format aus Abschnitt 2
- `watchdog_monitors.project_slug` — Kohortenbildung fuer den Flottenvergleich
- Taktgeber `supervisor_detectors` in `watchdog_cron_jobs`
Scopes in `TokenManager::KNOWN_SCOPES`, ergaenzt:
- `supervisor:report` — Befunde einliefern
- `supervisor:read` — Befunde lesen
- `supervisor:manage` — Befunde sichten und einstufen
- `source:read` — nur fuer Stufe 1, spaeter
`IMPLIED_SCOPES` entsprechend: `supervisor:report` und `supervisor:manage`
schliessen `supervisor:read` ein. Dass Melden und Einstufen getrennt sind, ist
Absicht: ein Beobachter auf Kundenhardware darf melden, aber nicht
entscheiden, dass seine eigene Meldung erledigt ist.
Etappe 1 brauchte keinen neuen Scope: die Unterscheidung laeuft ueber
`owner_type = 'dev_agent'`, den `TokenManager::OWNER_TYPES` bereits kannte.
---
## 9. Etappen
**Etappe 1 — MCP-Adapter ueber den Bestand. ERLEDIGT.**
Werkzeuge fuer alles, was heute schon existiert: `dc_projects`, `dc_next_task`,
`dc_get_item`, `dc_search`, `dc_report`, `dc_comment`, `dc_resolve`,
`dc_release`, `dc_error_groups` — in `mcp/deploymentcenter-mcp.mjs`, ohne
Fremdabhaengigkeiten. Dazu `dev_agent` in `enforceProject()`,
`dc_project_links`, `reported_from`, `evidence`, `triage_external`.
Kein Serverumbau, sofort nutzbar, ersetzt den Grossteil der Prompt-Vorlage.
Noch offen aus dieser Etappe: ein Werkzeug, mit dem der Agent
`integrated_version` fortschreibt (`ProjectLinks::record()` ist da, der
Endpunkt fehlt), und das Zurueckziehen der Prompt-Vorlage in den Projekten.
**Etappe 2 — Katalog und serverseitige Detektoren. ERLEDIGT.**
Katalog in `src/Modules/Supervisor/Catalog.php`, Ablage in `FindingRepo`,
Detektoren in `ServerDetectors` (haengen in `cli/tick.php`, laufen alle zehn
Minuten), Schnittstelle unter `/api/supervisor/v1/`, dazu `dc_findings`,
`dc_finding_summary` und `dc_metrics` im MCP-Adapter.
**Nutzen ohne eine einzige Client-Aenderung.**
Umgesetzt sind `novel.error_signature`, `silence.heartbeat`, `deviation.self`,
`deviation.fleet` und `regression.build`. Offen aus 1.1 bleibt
`novel.log_template` — dafuer muessten erst Logauszuege strukturiert
hereinkommen.
Eine Einschraenkung, die man kennen muss: `regression.build` vergleicht die
beiden hoechsten Fassungen im Feld im **Querschnitt**, also verschiedene
Maschinen zur selben Zeit — nicht dieselbe Maschine vor und nach dem Update.
Ein echter Laengsschnitt braeuchte eine Historie der `app_version` je Monitor;
das Feld wird heute ueberschrieben.
Voraussetzung fuer `deviation.fleet` und `regression.build`: Monitore brauchen
eine Projektzuordnung (`watchdog_monitors.project_slug`, neu in Migration 014).
Sie wird administrativ gesetzt, nicht vom Agenten gemeldet — sonst braeuchte
diese Etappe doch einen Rollout. Ohne Zuordnung laufen die uebrigen Detektoren
weiter; ihre Befunde landen unter `supervisor.default_project`.
**Etappe 3 — Supervisor-Client. ERLEDIGT.**
`.NET` in `client-dotnet/Deploymentcenter.Client/Supervisor*.cs`, PHP als
einzelne Datei in `client-php/DeploymentcenterSupervisor.php`, dazu
`docs/SUPERVISOR_INTEGRATION_GUIDE.md`.
Umgesetzt sind alle clientseitigen Detektoren aus 1.2 bis 1.4:
`trend.monotonic`, `trend.saturation`, `trend.latency`,
`swallowed.exception`, `fallback.usage`, `retry.storm`, `probe.failed`,
`probe.slow`, `silence.periodic`, `invariant.violated`.
Der PHP-Entwurf weicht notwendig ab: es gibt kein dauerhaft laufendes Programm,
in dem ein Beobachter mitlaufen koennte. Der Zustand liegt deshalb in einer
Datei unter Sperre, ausgewertet wird von der ersten Anfrage nach Ablauf des
Intervalls, gesendet wird nach `fastcgi_finish_request()`. Proben laufen
dadurch in einer echten Anfrage mit und muessen billig sein - das steht so in
der Anleitung.
Ausrollen ueber die Projekte, eines nach dem anderen.
**Etappe 4 — Symbol-Upload und `dc_symbolicate`.**
Packager erweitern, Symbolisierung serverseitig.
**Etappe 5 — Quelltext-Index Stufe 1.**
Nur falls ein autonomer Agent ohne laufende eigene Maschine gebraucht wird.
Bis dahin bewusst offen gelassen.
---
## 10. Offene Punkte
**Entschieden beim Umsetzen** (standen hier vorher als offen):
- Aufbewahrung der Befunde: 30 Tage in `FindingRepo::purge()`, aufgerufen am
Ende jedes Detektorlaufs. Deutlich kuerzer als bei Bugtracker-Items, weil
Befunde um Groessenordnungen haeufiger entstehen. Entfernt werden nur
erledigte, stummgeschaltete und uebernommene - ein offener Befund, der seit
Wochen dasteht, ist eine unbeantwortete Frage und soll unbequem bleiben.
- Kohortengroesse fuer `deviation.fleet`: fuenf, als `MIN_COHORT` in
`ServerDetectors`. Darunter ist die Streuung so grob geschaetzt, dass jeder
zweite Wert als Ausreisser durchginge.
- `probe.slow` ist ein eigener Detektor geblieben. Er vergleicht gegen das
gleitende Mittel der Probe selbst und nur ueber erfolgreiche Laeufe -
`deviation.self` haette dafuer den falschen Massstab.
**Weiterhin offen:**
- `novel.log_template` aus Abschnitt 1.1 - dafuer muessten erst Logauszuege
strukturiert hereinkommen.
- Ein Laengsschnitt fuer `regression.build`. Braucht eine Historie der
`app_version` je Monitor; das Feld wird heute ueberschrieben.
- Die Schwellen der clientseitigen Trenderkennung (zehn Prozent Zuwachs ueber
sechs Fenster) sind gesetzt, aber noch an keiner echten Installation
nachgeprueft.
> Wer hier etwas ergaenzt: Beim Umsetzen bitte auch wieder herausnehmen. Diese
> Liste stand einmal auf einem Stand, den der Code laengst ueberholt hatte, und
> hat prompt zu einer Fehlmeldung gefuehrt.