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,298 @@
|
||||
# Roadmap ins Deploymentcenter übernehmen
|
||||
|
||||
**Für Agenten und Entwickler in den angebundenen Projekten.** Diese Anleitung
|
||||
beschreibt, wie eine vorhandene `ROADMAP.md`, `TODO.md`, `PLAN.md` oder eine
|
||||
Sammlung von Entwurfsdokumenten in das Roadmap-System des Deploymentcenters
|
||||
überführt wird — einmalig, pro Projekt.
|
||||
|
||||
Verfügbar ab **2.7.0**. Voraussetzung: Migration `016_roadmap.sql` ist auf dem
|
||||
Server gelaufen.
|
||||
|
||||
---
|
||||
|
||||
## Warum überhaupt
|
||||
|
||||
Vorhaben lagen bisher in Markdown-Dateien: Status, Reihenfolge und
|
||||
Zuständigkeit von Hand gepflegt, neben einem Bugtracker, der genau diese drei
|
||||
Dinge bereits führt. Zwei Pfleger für dasselbe Feld — Drift war keine
|
||||
Möglichkeit, sondern eine Frage der Zeit.
|
||||
|
||||
Konkret ging dabei verloren:
|
||||
|
||||
- Ein Punkt stand als offen in der Datei, obwohl der Code ihn längst erledigt
|
||||
hatte. Wer die Datei las, arbeitete an etwas Fertigem.
|
||||
- Zwei Agenten nahmen sich dasselbe Vorhaben vor, weil die Datei nicht sagen
|
||||
kann, dass jemand daran sitzt.
|
||||
- Ein verworfener Vorschlag kam nach drei Monaten wieder, weil die Begründung
|
||||
in einem Entwurfsdokument stand, das niemand mehr aufschlug.
|
||||
|
||||
## Die Arbeitsteilung
|
||||
|
||||
**Kein Feld hat zwei Pfleger.** Das ist die ganze Regel; alles Weitere folgt
|
||||
daraus.
|
||||
|
||||
| | Wahrheit | Die andere Seite hält |
|
||||
|---|---|---|
|
||||
| Status, Reihenfolge, Zielfassung, Bearbeiter | **Deploymentcenter** | eine maschinell erzeugte Kopie zwischen Markern |
|
||||
| Begründung, Auslöser, Verworfenes, Herleitung | **Die Datei** | nur einen Verweis (`doc_anchor`) |
|
||||
|
||||
Die Datei verschwindet also **nicht**. Sie verliert nur die Hälfte, die sie
|
||||
ohnehin nie zuverlässig getragen hat.
|
||||
|
||||
## Die Kennung
|
||||
|
||||
Handvergebene Kennungen (`A12`, `TODO-7`) sind der Grund für die schlimmste
|
||||
Fehlerart: Beide Seiten sind für sich stimmig und meinen Verschiedenes, weil
|
||||
die Kennung nach einer Löschung neu vergeben wurde. Niemand bemerkt das.
|
||||
|
||||
Deshalb ist die Kennung die **Item-ID** aus dem Deploymentcenter.
|
||||
AUTO_INCREMENT vergibt nie zweimal dieselbe Nummer; eine gelöschte Zeile
|
||||
hinterlässt eine hängende Referenz, und die *meldet* der Abgleich, statt sie
|
||||
stillschweigend umzudeuten.
|
||||
|
||||
Der Themenbuchstabe bleibt — aber nur als Gruppe:
|
||||
|
||||
```markdown
|
||||
### [A] #142 — Symbol-Upload und dc_symbolicate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Übernahme in sechs Schritten
|
||||
|
||||
### 1. Bestandsaufnahme
|
||||
|
||||
Sortiere jeden Eintrag deiner Datei in genau eine der vier Kisten:
|
||||
|
||||
| Kiste | Kennzeichen | Wohin |
|
||||
|---|---|---|
|
||||
| **Vorhaben** | hat einen Zustand, jemand könnte es übernehmen | Item im DC |
|
||||
| **Zurückgestellt** | bewusst nicht jetzt, mit benanntem Anlass | Item (`deferred`) + Absatz |
|
||||
| **Verworfen** | geprüft und abgelehnt, mit Begründung | Item (`rejected`) + Absatz |
|
||||
| **Erledigt** | schon ausgeliefert | Item (`resolved`) — oder ersatzlos streichen |
|
||||
|
||||
Was in keine Kiste passt, ist meist ein Gedanke und kein Vorhaben. Der gehört
|
||||
in die Prosa, nicht in die Datenbank. **Faustregel:** Wenn niemand es
|
||||
übernehmen kann, ist es kein Item.
|
||||
|
||||
### 2. Themenbereiche anlegen
|
||||
|
||||
Die Abschnitte deiner Datei werden Themenbereiche. Kurzes Kürzel, sprechender
|
||||
Titel:
|
||||
|
||||
```bash
|
||||
curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/roadmap.php?action=epic" \
|
||||
-H "Authorization: Bearer $DC_TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"project_slug":"meinprojekt","epic":"A","title":"Handelslogik","sort_order":10}'
|
||||
```
|
||||
|
||||
Oder im WebUI unter **Roadmap → Themenbereiche**.
|
||||
|
||||
Halte die Zahl klein. Fünf bis zehn Bereiche sind übersichtlich, zwanzig sind
|
||||
eine zweite Ablage, die selbst wieder sortiert werden will.
|
||||
|
||||
### 3. Vorhaben anlegen
|
||||
|
||||
Jedes Vorhaben wird ein Item. Mit MCP-Adapter:
|
||||
|
||||
```
|
||||
dc_report(title: "Symbol-Upload und dc_symbolicate",
|
||||
type: "feature_request",
|
||||
description: "…",
|
||||
client_ref: "roadmap-a4")
|
||||
```
|
||||
|
||||
`client_ref` immer setzen: Ein wiederholter Lauf legt dann kein Duplikat an —
|
||||
und bei einer Übernahme von zwanzig Einträgen läuft irgendetwas immer zweimal.
|
||||
|
||||
Ohne MCP:
|
||||
|
||||
```bash
|
||||
curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/report" \
|
||||
-H "Authorization: Bearer $DC_TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"project_slug":"meinprojekt","type":"feature_request",
|
||||
"title":"…","description":"…","client_ref":"roadmap-a4"}'
|
||||
```
|
||||
|
||||
### 4. Auf die Roadmap heben
|
||||
|
||||
Ein Vorhaben steht auf der Roadmap, **sobald es einen Themenbereich hat**. Das
|
||||
ist das einzige Kennzeichen — ein zweites Flag daneben gäbe es zwei Angaben,
|
||||
die dasselbe behaupten und auseinanderlaufen können.
|
||||
|
||||
```
|
||||
dc_roadmap_set(id: 142, epic: "A", roadmap_rank: 10, target_build: "2.7.0",
|
||||
doc_anchor: "docs/ROADMAP.md#142-symbol-upload")
|
||||
```
|
||||
|
||||
Zur Reihenfolge: **Zehnerschritte.** Die Lücken sind Absicht — ein Eintrag
|
||||
lässt sich später auf Rang 15 dazwischenschieben, ohne dass alle folgenden neu
|
||||
nummeriert werden müssen.
|
||||
|
||||
Zurückgestelltes braucht den Auslöser, sonst wird der Aufruf abgelehnt:
|
||||
|
||||
```
|
||||
dc_roadmap_set(id: 147, epic: "A", status: "deferred",
|
||||
defer_trigger: "sobald Logauszüge strukturiert hereinkommen")
|
||||
```
|
||||
|
||||
Ohne benannten Anlass ist ein Vorhaben nicht zurückgestellt, sondern vergessen.
|
||||
Genau deshalb ist das Feld Pflicht und nicht bloß empfohlen.
|
||||
|
||||
### 5. Die Datei umbauen
|
||||
|
||||
Drei Änderungen:
|
||||
|
||||
**a) Zustandstabellen raus.** Jede von Hand gepflegte Tabelle mit Status-Spalte
|
||||
wird ersatzlos gelöscht. Sie kommt gleich erzeugt zurück.
|
||||
|
||||
**b) Überschriften auf Item-IDs umstellen.**
|
||||
|
||||
```markdown
|
||||
### [A] #142 — Symbol-Upload und dc_symbolicate
|
||||
|
||||
Der Packager lädt beim `publish` die PDB mit hoch, verknüpft mit `commit_sha`.
|
||||
Ausgeliefert wird ein gestripptes Binary. …
|
||||
```
|
||||
|
||||
Kein Status in der Überschrift, kein Häkchen, keine Zielfassung — das steht
|
||||
alles im erzeugten Block und wäre hier eine zweite Wahrheit.
|
||||
|
||||
**c) Den Block einsetzen.** Beim ersten Lauf wird er ans Dateiende gehängt;
|
||||
verschiebe ihn samt Markern dorthin, wo er stehen soll. Künftige Läufe finden
|
||||
ihn dort wieder.
|
||||
|
||||
```bash
|
||||
php cli/roadmap.php --write --project=meinprojekt --file=/pfad/zu/ROADMAP.md
|
||||
```
|
||||
|
||||
Liegt das Projekt auf einer anderen Maschine als das Deploymentcenter, holst du
|
||||
den Block über die API:
|
||||
|
||||
```bash
|
||||
curl -s "https://dc.mhdf.de/api/bugtracker/v1/roadmap.php?action=render&project_slug=meinprojekt" \
|
||||
-H "Authorization: Bearer $DC_TOKEN" | jq -r .markdown
|
||||
```
|
||||
|
||||
### 6. Abgleich einrichten
|
||||
|
||||
```bash
|
||||
php cli/roadmap.php --check --project=meinprojekt --file=/pfad/zu/ROADMAP.md
|
||||
```
|
||||
|
||||
Was geprüft wird — nur die **Prosa**, denn der erzeugte Block kann nicht
|
||||
abweichen:
|
||||
|
||||
| Befund | Bedeutung | Grad |
|
||||
|---|---|---|
|
||||
| `unknown_item` | Absatz nennt `#142`, das Item gibt es nicht | Fehler |
|
||||
| `foreign_item` | `#142` gehört einem anderen Projekt | Fehler |
|
||||
| `not_on_roadmap` | Absatz vorhanden, aber kein Themenbereich gesetzt | Warnung |
|
||||
| `title_drift` | Überschrift und Item-Titel haben nichts mehr gemeinsam | Warnung |
|
||||
| `decision_undocumented` | zurückgestellt/verworfen, aber keine Begründung in der Datei | Warnung |
|
||||
|
||||
Rückgabewert 0 heißt in Ordnung (Warnungen erlaubt), 1 heißt Fehler. Damit
|
||||
lässt sich der Aufruf in einen Deploy-Schritt oder Pre-Commit-Hook hängen.
|
||||
|
||||
Ein Item **ohne** Absatz ist bewusst kein Befund: Nicht jedes Vorhaben braucht
|
||||
eine Herleitung, die meisten sind eine Zeile wert und mehr nicht.
|
||||
|
||||
---
|
||||
|
||||
## Nach der Übernahme: die Regeln im Alltag
|
||||
|
||||
**Ein Vorhaben entsteht immer im DC, nie in der Datei.** `dc_report` legt das
|
||||
Item an → `--write` schreibt die Zeile → *danach* schreibst du die Begründung
|
||||
darunter. So kann ein Eintrag, der nur in der Datei steht, gar nicht erst
|
||||
entstehen — und damit auch kein offener Punkt, den niemand angeht.
|
||||
|
||||
**Die Arbeitsschlange ist die Wahrheit über Arbeit.** Was nicht über
|
||||
`?action=next` ausgegeben wird, macht niemand — unabhängig davon, wie es in
|
||||
einer Datei markiert ist. Ein Eintrag, der etwas werden soll, muss ein Item
|
||||
sein.
|
||||
|
||||
**Vor dem Anfangen `dc_roadmap` aufrufen.** Es beantwortet „was ist als
|
||||
Nächstes dran" und „arbeitet schon jemand daran". Beides, bevor du beginnst.
|
||||
|
||||
**Vor dem Wiedervorschlagen die Begründung lesen.** Steht ein Vorhaben auf
|
||||
`rejected` oder `deferred`, führt `doc_anchor` zu dem Absatz, der sagt warum.
|
||||
Ein erneuter Vorschlag braucht ein Argument, das dort noch nicht steht.
|
||||
|
||||
**Der erzeugte Block darf altern.** Er ist eine Kopie, und Kopien altern. Der
|
||||
Live-Stand kommt aus `dc_roadmap`; der Block ist für Menschen, die die Datei
|
||||
aufschlagen. `--check` weist auf einen Block hin, der älter als ein Tag ist.
|
||||
|
||||
---
|
||||
|
||||
## Was nicht ins DC gehört
|
||||
|
||||
Der Umzug ist keine Einladung, alles in die Datenbank zu kippen.
|
||||
|
||||
- **Architekturentscheidungen mit Herleitung** bleiben in der Datei. Ein
|
||||
Absatz, der eine Abwägung erklärt, überlebt keine Tabellenzelle.
|
||||
- **Verworfenes mit Begründung** bleibt in der Datei. Der Wert des Eintrags ist
|
||||
die Begründung, nicht die Ablehnung. Im DC steht nur der Status daneben.
|
||||
- **Lose Gedanken** gehören weder hierhin noch dorthin. Wenn niemand sie
|
||||
übernehmen kann, sind sie kein Vorhaben — im Zweifel als Notiz ins Archiv.
|
||||
- **Alte Entwurfsdokumente** nach `docs/archiv/` verschieben, nicht löschen.
|
||||
Sie enthalten die ausführliche Begründung, aber keinen gültigen Stand mehr.
|
||||
Ein Hinweis darauf gehört an den Kopf der Roadmap-Datei.
|
||||
|
||||
---
|
||||
|
||||
## Werkzeuge im Überblick
|
||||
|
||||
| Werkzeug | Wofür |
|
||||
|---|---|
|
||||
| `dc_roadmap` | lesen: was ist dran, wer sitzt dran (MCP) |
|
||||
| `dc_roadmap_set` | Bereich, Rang, Zielfassung, Anker setzen (MCP) |
|
||||
| `GET /api/bugtracker/v1/roadmap.php?action=list` | dasselbe über REST |
|
||||
| `?action=render` | den Markdown-Block holen |
|
||||
| `?action=check` | Abgleich mit mitgeschickter Datei (`content`) |
|
||||
| `php cli/roadmap.php --write\|--check\|--list` | von der DC-Maschine aus |
|
||||
| WebUI → **Roadmap** | Übersicht, Filter, Reihenfolge, Aufnahmeliste |
|
||||
|
||||
Token braucht `bugtracker:read` zum Lesen und `bugtracker:manage` zum
|
||||
Schreiben. Ein Werkbank-Token (`dev_agent`) hat beides.
|
||||
|
||||
---
|
||||
|
||||
## Beispiel: eine Datei vorher und nachher
|
||||
|
||||
**Vorher** — Zustand und Begründung durcheinander, beides von Hand:
|
||||
|
||||
```markdown
|
||||
## A — Supervisor
|
||||
|
||||
| | Vorhaben | Stand |
|
||||
|---|---|---|
|
||||
| A4 | Symbol-Upload | 📋 geplant für 2.7.0 |
|
||||
| A5 | Logzeilen-Schablonen | 🧊 später |
|
||||
|
||||
### A4 — Symbol-Upload
|
||||
Der Packager lädt die PDB mit hoch…
|
||||
```
|
||||
|
||||
**Nachher** — Zustand erzeugt, Begründung von Hand, verbunden über die ID:
|
||||
|
||||
```markdown
|
||||
<!-- dc:roadmap stand=2026-08-29T14:02Z items=7 projekt=deploymentcenter -->
|
||||
|
||||
### A — Supervisor und Befunde
|
||||
|
||||
| | Vorhaben | Stand | Ziel |
|
||||
|---|---|---|---|
|
||||
| #142 | Symbol-Upload und dc_symbolicate | 📋 Offen | `2.7.0` |
|
||||
| #143 | novel.log_template | 🧊 Zurückgestellt — Auslöser: sobald Logauszüge strukturiert hereinkommen | — |
|
||||
|
||||
<!-- /dc:roadmap -->
|
||||
|
||||
## Begründungen
|
||||
|
||||
### [A] #142 — Symbol-Upload
|
||||
Der Packager lädt die PDB mit hoch, verknüpft mit `commit_sha`. Ausgeliefert
|
||||
wird ein gestripptes Binary. Der Kunde bekommt `at 0x4a2f`, du bekommst
|
||||
`UserAuthService.cs:42` — ohne dass Quelltext das Haus verlässt.
|
||||
```
|
||||
|
||||
Die Statuszeile stimmt jetzt, weil sie niemand mehr pflegt.
|
||||
Reference in New Issue
Block a user