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:
co-authored by
Claude Opus 5
parent
9d0261306c
commit
1d5ac92051
@@ -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.
|
||||
Reference in New Issue
Block a user