Files
Deploymentcenter/public/docs/bugtracker.md
T
Deploymentcenter BotandClaude Opus 5 c9c821a44a fix(webui): Ansichtsrettung suchte am falschen Ort, Suche findet Nummern
#288 war mit dem Transport allein nicht behoben. Die Ansichtsrettung fragte
dokumentweit nach der offenen Unteransicht:

    document.querySelector('.subtab-content.active')

Im Markup tragen aber ACHT Unteransichten diese Klasse - jedes Modul hat seine
eigene Startansicht, und switchMainTab() raeumt nur innerhalb des Moduls auf,
das es oeffnet. Zurueck kam deshalb immer die erste in Dokumentreihenfolge
(sub-projects-list), voellig unabhaengig davon, was gerade sichtbar war. Wer im
Bugtracker filterte, waere also auf der Projektverwaltung gelandet.

Dass die Rettung fuer POST-Formulare zu funktionieren schien, lag an den
wenigen, die ihren Anker fest in der action stehen haben. Gesucht wird jetzt
erst das aktive Modul und darin die Unteransicht.

Zusaetzlich tragen die drei Filterformulare das Feld "view" fest im Markup.
Dort ist ohne Zweifel bekannt, wo sie stehen - das haengt an keiner DOM-Abfrage
mehr. Und restoreViewFromHash() faellt bei einer unbekannten Unteransicht auf
das Modul zurueck, statt wortlos im Dashboard zu landen.

Nachgewiesen an einer DOM-Probe, die die Modulstruktur aus index.php nachbaut
und die echten Funktionen darauf laufen laesst: alte Fassung -> tab-projects,
neue Fassung -> sub-bugtracker-items.

Ausserdem, aus derselben Sitzung gemeldet: Die Suche findet Eintraege jetzt
ueber ihre Nummer. Sie ist das, was ueberall zitiert wird, und stand in keiner
der durchsuchten Spalten - "288" lief ins Leere. "288" findet jetzt das Item
mit dieser Nummer und alles, was die Zahl im Text hat; "#288" ausschliesslich
dieses eine. Gilt fuer Bugtracker, Fehler-Stream und den Parameter search der
API, der bei der Gelegenheit auch in die OpenAPI-Beschreibung gekommen ist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 22:03:08 +02:00

18 KiB

Deployment Center — Agenten-Handbuch

Diese Seite beschreibt, wie ein Coding-Agent den Bugtracker und den UpdateService des Deployment Centers benutzt.

Maschinenlesbare Fassung: GET /api/openapi.json


0. Was sich geändert hat

Wer eine ältere Integration betreibt, muss zwei Dinge anpassen:

Änderung Auswirkung
POST /api/bugtracker/v1/report verlangt jetzt zwingend ein Token Aufrufe ohne Token liefern 401 unauthorized
GET /api/bugtracker/v1/projects verlangt jetzt ein Token dito
POST auf UpdateService-Publish verlangt updateservice:publish Aufrufe ohne Token liefern 401
Antwortformat vereinheitlicht Erfolg: {"status":"success",...}, Fehler: {"status":"error","error":{"code":"…","message":"…"}}

Der Feldname error_hash bleibt erhalten; zusätzlich gibt es dedup_key.


1. Authentifizierung

Alle Endpunkte akzeptieren das Token in einem dieser Header:

Authorization: Bearer dc_sub_xxxxxxxxxxxx
X-Agent-Token: dc_sub_xxxxxxxxxxxx

Token-Hierarchie

  • Master-Token (dc_master_…) — wird im WebUI unter Token-Verwaltung erzeugt. Langlebig, gehört auf den Rechner bzw. in die CI, nicht in ein Repository.
  • Sub-Token (dc_sub_…) — erzeugt sich ein Agent selbst aus dem Master-Token. Rechte lassen sich dabei nur einschränken, nie erweitern.

Sub-Token anfordern

curl -X POST https://dc.mhdf.de/api/tokens/v1/provision \
  -H "X-Master-Token: dc_master_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
        "client_name": "claude-code auf DEV-WORKSTATION-01",
        "instance_id": "DEV-WORKSTATION-01",
        "scopes": ["bugtracker:report", "bugtracker:read", "bugtracker:manage"],
        "environment": "development"
      }'

Das zurückgegebene sub_token wird nur einmal ausgeliefert.

Rechte (Scopes)

Scope Erlaubt
bugtracker:report Bugs, Feature Requests und Ideen melden
bugtracker:read Items und Projekte lesen
bugtracker:manage Übernehmen, kommentieren, Status setzen, schließen
watchdog:ping Heartbeats senden
updateservice:read Auf Updates prüfen
updateservice:publish Releases veröffentlichen
bugtracker:* alle Bugtracker-Rechte
* alles

Ist ein Token an ein Projekt gebunden, greifen alle Aufrufe automatisch nur auf dieses Projekt zu — ein Zugriff auf ein anderes liefert 403 project_forbidden.

Was kann mein Token? (ab 2.7.1)

curl https://dc.mhdf.de/api/tokens/v1/whoami \
  -H "Authorization: Bearer $DC_TOKEN"
{
  "status": "success",
  "name": "Werkbank polytrader",
  "type": "master",
  "owner_type": "dev_agent",
  "is_workbench": true,
  "project_slug": "polytrader",
  "environment": "all",
  "scopes": ["bugtracker:manage", "license:dev"],
  "last_used_at": "2026-08-30 09:12:44",
  "expires_at": null
}

Gedacht für Werkzeuge, die ihre Voraussetzungen prüfen wollen, bevor sie etwas tun. Ohne diese Auskunft ließe sich ein Recht nur durch Ausprobieren feststellen — und das hat Nebenwirkungen: eine Probe auf bugtracker:manage übernähme ein Item, eine auf license:dev stellte einen Schlüssel aus.

is_workbench ist der Wert, der über Fremdmeldungen entscheidet: nur ein Token der Besitzerart dev_agent darf in ein fremdes Projekt melden. Der Klartextwert des Tokens wird nicht ausgeliefert; ein widerrufenes oder abgelaufenes Token bekommt 401.


2. Projekte finden

curl https://dc.mhdf.de/api/bugtracker/v1/projects \
  -H "Authorization: Bearer $DC_TOKEN"
{
  "status": "success",
  "count": 4,
  "projects": [
    {
      "slug": "deploymentcenter",
      "name": "Deployment Center",
      "repo_url": "https://git.example.com/Richard/Deploymentcenter.git",
      "default_agent": null,
      "open_items": 3,
      "critical_items": 0
    }
  ]
}

Findest du einen Fehler im Deployment Center selbst, melde ihn unter project_slug: "deploymentcenter".


3. Etwas melden

curl -X POST https://dc.mhdf.de/api/bugtracker/v1/report \
  -H "Authorization: Bearer $DC_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: run-2026-08-07-42" \
  -d '{
        "project_slug": "myapp",
        "type": "bug",
        "title": "NullReferenceException in UserAuthService",
        "description": "Tritt beim Login ohne gesetzte Session auf.",
        "error_message": "Object reference not set to an instance of an object.",
        "stack_trace": "at MyApp.Core.UserAuthService.ValidateToken(String token)",
        "severity": "high",
        "environment": "production",
        "build_version": "v1.4.2",

        "repo_url": "https://git.example.com/me/myapp.git",
        "git_branch": "main",
        "commit_sha": "a21536f",
        "file_path": "src/Core/UserAuthService.cs",
        "line_no": 42
      }'

Felder

Feld Pflicht Bedeutung
title ja Kurze Beschreibung, max. 255 Zeichen
project_slug empfohlen Aus der Projektliste; Vorgabe default
type nein bug (Vorgabe) oder feature_request
severity nein idea, wishlist, low, medium (Vorgabe), high, critical
environment nein production (Vorgabe), development, staging, testing
client_ref empfohlen Idempotenz-Schlüssel, alternativ Header Idempotency-Key
repo_url, git_branch, commit_sha, file_path, line_no empfohlen Code-Kontext — spart dem nächsten Agenten das Parsen des Stacktrace
context nein Beliebiges JSON-Objekt für Zusatzinformationen
push_id, target_agent, tags nein Workflow-Zuordnung

created_by wird aus dem Token abgeleitet und kann nicht gesetzt werden.

Was der Server daraus macht

  • Deduplizierung — gleiche Fehler werden zusammengefasst und occurrence_count erhöht. Zeilennummern, Speicheradressen, GUIDs und Zeitstempel werden dabei ausgeblendet, damit derselbe Fehler nicht als neu gilt. Feature Requests und Ideen werden über den Titel dedupliziert.
  • Eskalation — wird ein offener Bug erneut mit höherem Schweregrad gemeldet, wird er hochgestuft (nie herabgestuft).
  • Regression — tritt ein bereits gelöster Bug erneut auf, entsteht ein neues Item mit regression_of als Verweis auf das alte.
  • Idempotenz — identische client_ref im selben Projekt legt kein Duplikat an.

Antwort

{
  "status": "success",
  "item_id": 42,
  "is_new": true,
  "idempotent_hit": false,
  "occurrence_count": 1,
  "dedup_key": "e2c918a514d89a42f...",
  "item_status": "open",
  "regression_of": null,
  "message": "Bug erfasst."
}

4. Die Agenten-Schleife

Basis: https://dc.mhdf.de/api/bugtracker/v1/manage

4.1 Arbeit holen und übernehmen

Ein Aufruf, der die nächsten offenen Items liefert und exklusiv für dich reserviert — damit arbeiten nicht zwei Agenten am selben Bug:

curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/manage?action=next" \
  -H "Authorization: Bearer $DC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"project_slug": "myapp", "limit": 1, "severity": "critical,high"}'

Die Reservierung (Lease) läuft nach 30 Minuten automatisch ab. Brauchst du länger, erneuere sie mit action=claim auf dieselbe ID.

4.2 Zwischenstand dokumentieren

curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/manage?action=comment&id=42" \
  -H "Authorization: Bearer $DC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "comment": "Ursache gefunden: Session wird vor dem Redirect nicht initialisiert.",
        "action_taken": "investigated"
      }'

Empfohlene Werte für action_taken: investigated, fix_proposed, pr_opened, needs_human, blocked, commented.

4.3 Abschließen

curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/manage?action=resolve&id=42" \
  -H "Authorization: Bearer $DC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "resolved_in_build": "v1.4.3",
        "resolution_notes": "Session-Initialisierung in AuthController vorgezogen."
      }'

4.4 Wieder freigeben

Kommst du nicht weiter, gib das Item zurück, statt den Lease verfallen zu lassen:

curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/manage?action=release&id=42" \
  -H "Authorization: Bearer $DC_TOKEN" \
  -d '{"note": "Benötigt Zugriff auf Produktivlogs."}'

5. Lesen und Filtern

curl "https://dc.mhdf.de/api/bugtracker/v1/manage?action=list&project_slug=myapp&status=open,in_progress&order=severity&limit=20" \
  -H "Authorization: Bearer $DC_TOKEN"
Parameter Bedeutung
status, severity Mehrere Werte kommagetrennt
type, environment, project_slug Einzelwert oder all
target_agent, claimed_by, push_id Exakte Übereinstimmung
search Volltext über Titel, Beschreibung, Fehlermeldung, Tags, Dateipfad. Eine Zahl findet zusätzlich das Item mit dieser Nummer, #288 ausschließlich dieses eine
unclaimed_only true — nur Items, die kein Agent bearbeitet
updated_since ISO-8601 — Delta-Abfrage für effizientes Polling
order newest, oldest, updated, severity, occurrences
limit, offset Pagination, max. 500 pro Seite

Die Antwort enthält total, limit, offset und has_more.

Polling-Muster

# Nur was sich seit dem letzten Durchlauf geändert hat
curl "…/manage?action=list&updated_since=2026-08-07T09:00:00Z&order=updated" \
  -H "Authorization: Bearer $DC_TOKEN"

6. Release veröffentlichen und Items automatisch schließen

Der Kreis schließt sich hier: Items, deren resolved_in_build der veröffentlichten Version entspricht, werden beim Publish automatisch geschlossen.

curl -X POST https://dc.mhdf.de/api/updateservice/v1/publish \
  -H "Authorization: Bearer $DC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "product_slug": "myapp",
        "version": "1.4.3",
        "channel": "prod",
        "download_url": "https://dc.mhdf.de/downloads/myapp-1.4.3.zip",
        "sha256_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
        "git_commit": "a21536f",
        "release_notes": "Behebt den Login-Fehler."
      }'
{
  "status": "success",
  "release_id": 12,
  "created": true,
  "auto_resolved": 3,
  "message": "Release 1.4.3 (prod) für \"myapp\" veröffentlicht. 3 Bugtracker-Item(s) automatisch geschlossen."
}

Der Versionsvergleich folgt der semantischen Versionsordnung — 1.10.0 gilt korrekt als neuer als 1.9.0.


7. Fehlerbehandlung

Fehler tragen einen stabilen, maschinenlesbaren Code. Reagiere auf code, nicht auf message:

{
  "status": "error",
  "error": { "code": "already_claimed", "message": "Item #42 ist bereits vergeben." }
}
Code HTTP Bedeutung und Reaktion
unauthorized 401 Token fehlt, ist abgelaufen oder hat den Scope nicht
project_forbidden 403 Token ist an ein anderes Projekt gebunden
already_claimed 409 Anderer Agent arbeitet daran — nächstes Item nehmen
not_claimed 409 Freigabe eines Items, das dir nicht gehört
not_found 404 Item existiert nicht
rate_limited 429 Sendefrequenz senken, später erneut
invalid_json 400 Request-Body ist kein gültiges JSON
missing_id, missing_status, missing_build 400 Pflichtfeld fehlt
invalid_status, invalid_version, invalid_hash 400 Wert nicht zulässig
internal_error 500 Serverfehler — wird automatisch selbst im Bugtracker erfasst

Rate-Limit: 60 Reports pro Minute und IP. Bei 429 das Intervall verdoppeln.


8. Vollständige Beispielschleife (Python)

import os, requests

BASE = "https://dc.mhdf.de/api/bugtracker/v1/manage"
HEAD = {"Authorization": f"Bearer {os.environ['DC_TOKEN']}",
        "Content-Type": "application/json"}


def next_item(project):
    r = requests.post(f"{BASE}?action=next", headers=HEAD,
                      json={"project_slug": project, "limit": 1})
    r.raise_for_status()
    items = r.json().get("items", [])
    return items[0] if items else None


def comment(item_id, text, action="investigated"):
    requests.post(f"{BASE}?action=comment&id={item_id}", headers=HEAD,
                  json={"comment": text, "action_taken": action}).raise_for_status()


def resolve(item_id, build, notes):
    requests.post(f"{BASE}?action=resolve&id={item_id}", headers=HEAD,
                  json={"resolved_in_build": build,
                        "resolution_notes": notes}).raise_for_status()


def release(item_id, reason):
    requests.post(f"{BASE}?action=release&id={item_id}", headers=HEAD,
                  json={"note": reason}).raise_for_status()


item = next_item("myapp")
if item is None:
    print("Nichts zu tun.")
else:
    print(f"#{item['id']}: {item['title']}")
    if item.get("file_path"):
        print(f"  -> {item['file_path']}:{item.get('line_no', '?')}")

    comment(item["id"], "Analyse gestartet.")
    try:
        # ... hier die eigentliche Arbeit ...
        resolve(item["id"], "v1.4.3", "Fix in AuthController.")
    except Exception as exc:
        release(item["id"], f"Abbruch: {exc}")

8a. Fehler melden (Laufzeitfehler)

Für den globalen Exception-Handler einer Anwendung gibt es einen schlankeren Eingang. Titel und Dringlichkeit leitet der Server ab:

curl -X POST https://dc.mhdf.de/api/errors/v1/report \
  -H "Authorization: Bearer $DC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "project_slug": "polytrader",
        "exception":    "PDOException",
        "message":      "SQLSTATE[23000]: Duplicate entry '\''MKT-88213'\'' for key '\''uq_market'\''",
        "stack_trace":  "at Importer.php:142",
        "level":        "error",
        "build":        "v2.0.1",
        "environment":  "production",
        "file":         "src/Market/Importer.php",
        "line":         142
      }'

level unterscheidet die technische Art des Ereignisses — unabhängig von der geschäftlichen Dringlichkeit:

Wert Bedeutung
fatal Der Prozess hat sich beendet
error Ein Vorgang ist fehlgeschlagen, das Programm läuft weiter (Vorgabe)
warning Auffälligkeit ohne Funktionsverlust

Gruppierung

Gleiche Fehler werden zu einer Gruppe zusammengefasst und hochgezählt. Veränderliche Bestandteile werden dabei ausgeblendet — Werte in Anführungszeichen, die Ziffern enthalten, Speicheradressen, GUIDs, Zeitstempel und Zeilennummern. Diese drei Meldungen ergeben eine Gruppe:

Duplicate entry 'MKT-88213' for key 'uq_market'
Duplicate entry 'AA-1'      for key 'uq_market'
Duplicate entry 'X-99471'   for key 'uq_market'

Ein anderer Unique-Key (uq_orders) bleibt dagegen eine eigene Gruppe — der Schlüsselname enthält keine Ziffern und zählt damit zur Identität des Fehlers.

Bekannte, harmlose Fehler

Manche Fehler treten betriebsbedingt auf und sind belanglos. Dafür gibt es Ignore-Regeln, die im WebUI unter Bugtracker → Ignore-Regeln gepflegt werden. Greift eine Regel, wird der Fehler weiterhin erfasst und hochgezählt, bleibt aber aus der Übersicht heraus und löst keine Benachrichtigung aus:

{ "status": "success", "item_id": 42, "ignored": true,
  "ignore_rule_id": 3, "occurrence_count": 3841, "rate_alerted": false,
  "message": "Als bekannt eingestuft, gezaehlt, nicht gemeldet." }

Der Zähler ist dabei der eigentliche Zweck: Zu jeder Regel lässt sich eine Alarmschwelle hinterlegen. Dass ein bekannter Fehler auftritt, ist normal — dass er plötzlich hundertmal so oft auftritt, ist ein Signal. Wird die Schwelle überschritten, meldet die Antwort "rate_alerted": true und ein Webhook error.rate_exceeded wird ausgelöst.


9. Watchdog-Heartbeat

Läuft dein Agent als Dienst, melde dich regelmäßig:

curl -X POST https://dc.mhdf.de/api/watchdog/v1/ping \
  -H "Authorization: Bearer $DC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"source": "agent-worker-01", "status": "ok", "interval": 60,
       "message": "Verarbeite Warteschlange", "metrics": {"queue": 3}}'

interval ist der erwartete Abstand in Sekunden. Bleibt der Heartbeat aus, stuft der Evaluator den Monitor nach dem Doppelten auf warning und nach dem Vierfachen auf down.

Eigenen Gesundheitszustand mitsenden

Ein Heartbeat beweist nur, dass ein Thread läuft — nicht, dass die Anwendung ihre Arbeit tut. Deshalb kann sie ihren Zustand selbst mitschicken:

{ "source": "polytrader-worker",
  "status": "warning",
  "interval": 60,
  "checks": {
    "db":          { "ok": true },
    "market_feed": { "ok": false, "message": "Letzter Tick vor 14 min" },
    "queue":       { "ok": true, "value": 23 }
  },
  "metrics": { "cpu": 18, "ram": 42, "queue_depth": 23 } }

Das Deploymentcenter interpretiert die Namen der Prüfungen nicht — es liest nur ok und message. Was „gesund" bedeutet, entscheidet jede Anwendung selbst. Schlägt eine Prüfung fehl, wird ein als ok gemeldeter Heartbeat auf warning herabgestuft.

Der Weg ist bewusst ausgehend: Es müssen keine Ports auf den Zielmaschinen geöffnet werden.

Metriken

Numerische Werte aus metrics landen im Verlauf und lassen sich abfragen:

# Welche Metriken liefert dieser Monitor?
curl "https://dc.mhdf.de/api/watchdog/v1/metrics?source=polytrader-worker" \
  -H "Authorization: Bearer $DC_TOKEN"

# Verlauf einer Metrik, auf 15-Minuten-Fenster verdichtet
curl "https://dc.mhdf.de/api/watchdog/v1/metrics?source=polytrader-worker&metric=queue_depth&hours=24" \
  -H "Authorization: Bearer $DC_TOKEN"

Die Antwort enthält zusätzlich deviation — den Vergleich des aktuellen Werts mit dem Durchschnitt der letzten sieben Tage desselben Monitors. Damit lassen sich Auffälligkeiten erkennen, ohne für jedes Projekt Schwellwerte zu pflegen.

Verschachtelte Werte werden flach abgelegt: {"cpu":{"load":1.2}} wird zu cpu.load. Rohwerte werden 14 Tage aufbewahrt.

Abhängigkeiten

Ist bei einem Monitor parent_source gesetzt und fällt der übergeordnete Monitor aus, werden Alarme für die Kinder unterdrückt. Ihr Zustand bleibt im Dashboard sichtbar — es entsteht nur nicht für jede VM eines ausgefallenen Hypervisors eine eigene Meldung.


10. Verfügbarkeit prüfen

curl https://dc.mhdf.de/api/health -H "Authorization: Bearer $DC_TOKEN"

Meldet Datenbankzustand, ausstehende Migrationen, Bugtracker-Kennzahlen und wann der Watchdog-Evaluator zuletzt lief.