Files
Deploymentcenter/docs/archiv/SUPERVISOR_MCP_DESIGN.md
T
Deploymentcenter BotandClaude Opus 5 1d5ac92051 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>
2026-08-29 21:59:20 +02:00

26 KiB

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 ü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
{
  "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

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:

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.