adr vertraulich owner: matus
ADR-0006: Feedback-Triage als eigenes Prozessmodul — und ausdrücklich *nicht* „Autoimprove"
ADR-0006: Feedback-Triage als eigenes Prozessmodul — und ausdrücklich nicht „Autoimprove"
Status
Abgelöst durch ADR-0007 (2026-08-14).
Angenommen am 2026-08-13, abgelöst am Tag darauf. Das hier beschriebene Modul
tuxametrics-feedback-process ist ersatzlos entfernt; zwirn-feedback-service wird in
tuxametrics nicht mehr betrieben.
Der Inhalt unten bleibt unverändert stehen — er ist ab „Kontext" nur noch als Beleg lesbar, nicht mehr als Beschreibung des Systems. Der Grund für die Ablösung steht vollständig in ADR-0007 und ist unangenehm: Abschnitt 6 („Was hier bewusst nicht entsteht") begründet den Verzicht auf „Autoimprove" mit DOTs
konzepte/agentic-orchestration.md— einem im Quellrepo selbst als „nicht gebaut, zurückgestellt" markierten Visionspapier. Das war die falsche Quelle. Der kanonischezwirn-autoimprove-serviceexistiert in Zwirn seit Längerem, ist gebaut, getestet und capability-gegatet; er wurde bei der Recherche schlicht nicht gefunden. Die Tabelle in Abschnitt 6 ist damit in ihrer Kernaussage falsch: „Autoimprove" war keine unerprobte Vision, sondern ein fertiger Service, den anzuschließen weniger Code gekostet hätte als das hier beschriebene Modul.Was richtig bleibt und in ADR-0007 fortgeführt wird: die Trennung von Vorgang und Sammelstelle (Kontext), die Tenant-Pfad-Regel für
zwirn.security.tenant-path-pattern(§2), und dass keine der fünf Leitplanken ausCLAUDE.mdberührt ist (§6, letzter Absatz).
Kontext
Seit dem 2026-08-09 betreibt tuxametrics zwirn-feedback-service als fertiges Docker-Image
(Zwirn-ADR-0006): das In-App-Widget „Bug/Idee zu diesem Screen melden" aus @zwirn/feedback, in
tuxametrics-ui/src/shell/TopNav.tsx auf jeder Seite eingebunden. Der Service kennt einen
Status-Workflow OPEN → IN_PROGRESS → RESOLVED — aber niemand hat ihn getrieben. Jede Meldung
blieb auf OPEN stehen, für immer, ohne Aufgabe und ohne Zuständigen. Der Rückkanal aus einer
Pilotpraxis war damit eine Sammelstelle, kein Vorgang.
Dieselbe Datei, die das entschieden hat, hat auch benannt, was fehlt:
audit-feedback-processhub-integration.md
Z. 37–40 — „Nicht angeschlossen: zwirn-autoimprove-service. […] Erst Feedback sammeln, dann
entscheiden, ob es eine Pipeline braucht."
Zwei Dinge sind seither dazugekommen, und beide sind der Anlass für dieses ADR:
- Es gibt ein belegtes Vorbild für die zweite Stufe, und es ist keine Agenten-Pipeline.
DOT-ADR-0030 schneidet für dieselbe
Domäne einen Data Service und ein Prozessmodul:
Start → UserTask „Feedback sichten" → ServiceTask setzt IN_PROGRESS → UserTask „Feedback bearbeiten" → ServiceTask setzt RESOLVED → Ende, gestartet vom Frontend nach dem erfolgreichenPOST, abgesichert durch einen@Scheduled-Nachzug-Job. Gebaut, in Betrieb, nicht Vision. - Die Rahmenregel für neue Prozessmodule wurde einen Tag vor dieser Entscheidung geändert.
GG-ARCH-PROCESS-0001(../../../zwirn/guidelines/architecture/process/microprocess-architecture.md, Abschnitt „Pflicht beim Anlegen eines neuen Process-Moduls") verlangt seit DOT-ADR-0081 (2026-08-12) von jedem neuen Modul zweierlei: die engine-agnostische Prozess-API (ProcessObservationPortausdev.zwirn:zwirn-process-api) und einen Eintrag in der Endpunkt-Registry der Plattform. Wer jetzt baut, baut danach — oder er baut das nach.
Der Auslöser dieser Arbeit war die Bitte, „Autoimprove mit den aktuellsten Ideen aus DOT" umzusetzen. Die Prüfung dieser Bitte ist Teil der Entscheidung und steht deshalb unten unter „Was hier bewusst nicht entsteht".
Entscheidung
Ein neues, fünftes Maven-Modul: tuxametrics-feedback-process. Spring Boot, eingebettete
Flowable-8-Engine, eigene Postgres-Engine-DB, Port 8128.
1. Der Prozess
src/main/resources/processes/feedback-report.bpmn, Prozess-ID
io.dl.tuxametrics.feedback.v1.public.feedback-report (GG-ARCH-PROCESS-0002.1), Name
„Feedback-Meldung bearbeiten":
● Feedback gemeldet ─▶ (Feedback sichten) ─▶ [Status → IN_PROGRESS]
─▶ (Feedback bearbeiten) ─▶ [Status → RESOLVED] ─▶ ● Meldung erledigt
( ) = userTask, [ ] = serviceTask. Die beiden Entscheidungsschritte sind Aufgaben für
Menschen; die beiden Service-Schritte tun genau eine Sache — den Status im Data Service nachziehen,
per flowable:expression auf eine plain Spring-Bean (Zwirn-ADR-0033, kein JavaDelegate).
Ohne flowable:candidateGroups. Wer in einer Praxis eine Meldung sichtet, ist eine offene
fachliche Rollenfrage (OF-12). GG-ARCH-PROCESS-0003 führt Candidate Groups ausdrücklich als „was
das explizit NICHT abdeckt" — alle Aufgaben sind für alle authentifizierten Nutzer eines Mandanten
sichtbar. Eine erfundene Gruppe wäre eine Rollenaussage ohne Fundstelle.
2. Prozessstart: der UI-Trigger existiert bereits — es fehlte nur der Empfänger
@zwirn/feedback ruft in FeedbackReportModal.tsx nach dem erfolgreichen POST zusätzlich
POST /api/fbp/{tenantKey}/processes/feedback-report/instances — best effort, jeder Fehler wird
verschluckt (startFeedbackProcess(...).catch(() => {})). In tuxametrics lief dieser Aufruf bis heute
ins Leere: es gab nichts dahinter und keine Proxy-Route dorthin.
Entschieden ist deshalb kein neuer Trigger, sondern das Anschließen des vorhandenen: eine
location-Regel in tuxametrics-ui/nginx-locations.conf (und die gleichlautende im Vite-Dev-Proxy).
/api/fbp ist die Pfadkonvention des npm-Pakets; das Modul serviert unter
/api/v1/tenants/{tenantKey}/…, weil nur diese Form zwirn.security.tenant-path-pattern
(/tenants/([^/]+)) trifft. Umgeschrieben wird am Rand, nicht im Backend — dieselbe Regel, die für
/flowable-api schon gilt.
Das ist kein Stilthema. Ein Pfad ohne
/tenants/-Segment lässt ZwirnsUserContextFilterstill ins Leere laufen (die Mismatch-Prüfung beginnt mitpathTenantKey != null) — exakt die Lücke, die dieses Repo für Flowables rohe/process-api/**bereits dokumentiert hat und wegen der der Cockpit-Container auf Loopback gebunden wurde. Ein neues Modul reißt sie nicht ein zweites Mal auf.
Ein Data Service ruft nie ein Prozessmodul (GG-ARCH-PROCESS-0001). Hier ist das nicht einmal
theoretisch möglich: der Feedback-Service ist ein fertiges Fremd-Image, das dieses Produkt nicht kennt.
3. Nachzug statt Hoffnung: FeedbackProcessReconciliationScheduler
Alle zwei Minuten, je Mandant: offene Reports holen, für jeden ohne Prozessinstanz eine starten. Das ist die Gegenmaßnahme zum verschluckten Fehler oben — ohne sie wäre eine verlorene Meldung unsichtbar verloren. Die Leserichtung Prozessmodul → Data Service ist die erlaubte.
Der Start ist idempotent auf dem businessKey (FeedbackProcessStarter.startIfAbsent), geprüft
gegen die historische Instanzabfrage (laufende und beendete). UI-Trigger und Nachzug können sich
sonst überschneiden — ein Report bleibt OPEN, bis der erste UserTask abgeschlossen ist, also im
Regelfall tagelang. DOT hat diese Prüfung nur im Scheduler; hier liegt sie an einer Stelle, die
beide benutzen.
4. Die zwei Pflichten aus GG-ARCH-PROCESS-0001 — eine erfüllt, eine ehrlich offen
| Pflicht | Stand |
|---|---|
ProcessObservationPort implementieren (Dependency zwirn-process-flowable, OwnedProcessDefinitions-Bean, die drei zwirn.process.*-Properties) |
Erfüllt. config/OwnedFeedbackProcessDefinitions, zwirn.process.module-key=feedback-process. ProcessDeploymentSmokeTest prüft, dass der Port tatsächlich entsteht — und dass die deklarierte Menge der deployten entspricht (der Abgleich, den das Javadoc von OwnedProcessDefinitions verlangt) |
Eintrag in der Endpunkt-Registry der Plattform (hub.process-services: Routing-Key → Basis-URL) |
Nicht möglich — es gibt keine. Der aggregierende Hub ist ein DOT-Modul (dot-process-hub); tuxametrics betreibt mit tuxametrics-process-hub-cockpit Flowables rohe REST-Fassade, keinen Router mit Registry. service-landscape.md führt „Process-Hub-Aggregator: existiert nirgends" seit 2026-08-09 als offenen Punkt |
Der Routing-Schlüssel steht trotzdem gesetzt in application.yaml. Er ist die Zusage, unter der das
Modul gefunden wird, sobald es einen Router gibt — nicht eine Behauptung, dass es ihn gibt.
Praktische Folge, ungeschönt: es gibt in diesem Produkt heute keine Oberfläche, in der jemand
die Aufgaben sieht. Die drei @zwirn/bpmn-Seiten, die das könnten (ProcessInstancesPage,
ProcessTasksPage, TaskDetailPage), sind bewusst nicht gemountet — sie brauchen genau diesen
Aggregator.
Nicht zu verwechseln mit „unbedienbar" — dieser Fehler wäre beim ersten Entwurf beinahe passiert.
Die generische Prozess-API aus zwirn-process-api ist ausdrücklich rein lesend; das Cockpit auf
8124 hat eine eigene Engine und eigene DB und sieht diese Aufgaben nicht; und Flowables rohe
REST-API bringt dieses Modul bewusst nicht mit. Ohne einen eigenen Abschluss-Weg wäre der Prozess
gestartet, aber von niemandem weiterzutreiben gewesen — die Meldung bliebe trotz laufender Instanz
auf OPEN, und das ganze Modul wäre Dekoration.
4a. Deshalb Teil dieser Entscheidung: der Aufgaben-Lebenszyklus
controller/ProcessTaskController + service/ProcessTaskService implementieren die Task-API, die
GG-ARCH-PROCESS-0003 vom besitzenden Prozessmodul erwartet:
GET /api/v1/tenants/{tenantKey}/process-tasks
POST /api/v1/tenants/{tenantKey}/process-tasks/{taskId}/claim
POST /api/v1/tenants/{tenantKey}/process-tasks/{taskId}/unclaim
POST /api/v1/tenants/{tenantKey}/process-tasks/{taskId}/_complete
Drei Zusagen, jede einzeln getestet: der assignee kommt immer aus dem Token und nie aus einem
Body (das ausdrückliche „Falsch"-Beispiel der Guideline); jede Abfrage ist doppelt eingeschränkt
(nativer taskTenantId und OwnedProcessDefinitions); und die drei Zustände unclaimed / claimed
von mir / claimed von anderem verhalten sich wie in der Guideline (409 ohne Claim, 403 bei fremder
Zuweisung, 404 statt 403 für eine fremde Praxis — ob dort eine Aufgabe existiert, geht den
Aufrufer nichts an).
In DOT übernimmt diese Vermittlung dot-process-hub, weil nur er den End-User-JWT hält. Den Hub gibt
es hier nicht — dafür liegt der echte JWT ohnehin direkt an diesem Modul an, weil das Frontend über
denselben nginx spricht. Dieselbe Fläche ist zugleich genau das, was ein künftiger Hub erwartet:
„eigene generische Task-API implementieren + Eintrag in hub.process-services", mehr verlangt die
Guideline nicht.
Bedienbar ist die Triage damit heute per REST-Client, nicht per Klick. Das ist ein benannter Zustand, kein Versehen — und ein anderer als „läuft, aber steht für immer".
5. Sicherheit: eigener Service-Account, kein txm.*
- Prozessstart (aus dem Browser, mit User-JWT):
capabilityGuard.require("feedback.report.create")— der bestehende Schlüssel des Feedback-Service, nicht ein neuer. Der Prozessstart ist Teil derselben Nutzergeste wie das Melden; genau diese Begründung führtzwirn-feedback-servicebereits für seinen Screenshot-Upload an. Ein eigener Key müsste je Praxis gegrantet werden, und bis dahin liefe der Start in ein 403, das das Frontend verschluckt — der Fehler wäre unsichtbar. - Statuswechsel (aus der Engine, ohne Request): Service-Token. Der Zielendpunkt ist doppelt gegatet
(Capability
feedback.report.set-statusundactor_type=SERVICE). - Eigener Keycloak-Client
tuxametrics-feedback-processstatt des geteiltentuxametrics-services. Das ist die einzige bewusste Abweichung von einer bestehenden Vereinfachung dieses Repos, und der Grund ist konkret: der geteilte Actor trägt jeden Grant für jeden seiner Nutzer (keycloak/README.mdnennt diesen Preis selbst).feedback.report.set-statusauf ihm gegrantet hielte auch der Laborauswertungs-Service. Ein zweiter Client kostet 40 Zeilen im Realm-Export — der billigere Preis, und der erste Schritt in die Richtung (Zwirn-ADR-0039), in die die Vereinfachung ohnehin aufgelöst werden muss. - Aufgaben-Lebenszyklus:
feedback-process.task.read(Liste sehen) undfeedback-process.task.complete(übernehmen / zurückgeben / abschließen). Ein Schlüssel für alle drei Aktionen: wer abschließen darf, muss zwangsläufig claimen dürfen, und ein eigenes Recht fürs Zurückgeben wäre eine Falle — man könnte sich eine Aufgabe holen und sie nicht wieder loswerden. - Keine einzige
txm.*-Capability berührt. Die sechs eigenen Schlüssel des Moduls tragen das Präfixfeedback-process.*. Vier davon sind die abgeleiteten Lese-Rechte der generischen Prozess-API, zwei die Aufgaben-Rechte oben. Bewusst auch nichtfeedback.*: das ist der Namensraum des kanonischenzwirn-feedback-service, der seine Schlüssel selbst registriert.
6. Was hier bewusst nicht entsteht — und warum der Name „Autoimprove" nicht fällt
Der Auftrag lautete, „Autoimprove" umzusetzen. Das Ergebnis ist deutlich kleiner, und das ist eine Entscheidung, keine Auslassung:
| Gefordert/erwartet | Gebaut | Warum nicht mehr |
|---|---|---|
zwirn-autoimprove-service anschließen |
Nein, unverändert nicht angeschlossen | Die Entscheidung von 2026-08-09 („erst Feedback sammeln") ist damit nicht aufgehoben, sondern erst jetzt begehbar: bis heute gab es keinen Vorgang, den eine zweite Stufe hätte verarbeiten können. Der Service bleibt außen vor |
Agenten-Pipeline über Feedback-Karten (DOT konzepte/agentic-orchestration.md) |
Nein | Im Quellrepo selbst als „Nicht gebaut … zurückgestellt" markiert, in einem Ordner, der als „Frühe Architektur-Vision, nicht das tatsächlich Gebaute" überschrieben ist. Eine unerprobte Vision in ein Produkt mit Gesundheitsdaten-Anspruch zu übernehmen, wäre genau die Art von Übernahme, die dieses Repo an anderer Stelle verweigert |
| LLM-gestützte Bewertung/Priorisierung von Meldungen | Nein | Kein LLM, kein Agent, keine automatische Bewertung. Die zwei Entscheidungsschritte sind UserTasks |
Dieses Modul heißt deshalb nicht „autoimprove" und darf auch nicht so genannt werden. Es ist eine Triage: zwei menschliche Schritte und zwei Statuszeilen. Wer es „Autoimprove" nennt, verspricht eine Pipeline, die es nicht gibt — dieselbe Sorte Etikett wie „das Cockpit zeigt Prozesse", solange die Engine leer ist.
Keine der fünf Leitplanken aus CLAUDE.md ist berührt. Geprüft, einzeln:
GM-7 (kein klinischer Entscheidungspfad, keine der drei geschützten Capabilities, Gm7Schutz
unverändert), die vier unbequemen Elemente (kein Fachdatenmodell angefasst), E2 (kommt nicht vor),
Medikamenten-Interaktionen (nicht berührt), die zwei externen Fragen (UNGEKLAERT, RegulatorikGuard
unverändert). Es entsteht keine neue Fachzahl und kein neuer Enum-Wert der Domäne. Die Leitregel
„Korrigiert wird, was v1.2 widerlegt" ist nicht berührt: das hier ist Infrastruktur um den Ablauf
herum, wie schon Audit/Feedback/Cockpit — eine betriebliche Scope-Erweiterung.
Konsequenzen
Was besser wird
- Eine Meldung aus der Pilotpraxis hat ab jetzt einen Vorgang, einen Status, eine Historie und einen Zeitpunkt — statt eine Zeile in einer Tabelle zu sein, die niemand anfasst.
- Der Reactor prüft ein fünftes Modul mit; Orchestrierung und Aufgaben-Lebenszyklus sind getestet (17 Tests, echte Engine).
- tuxametrics erfüllt die neue Pflicht aus
GG-ARCH-PROCESS-0001beim ersten Modul, das ihr unterliegt — statt sie später nachzuziehen.
Was es kostet
| vorher | nachher | |
|---|---|---|
| Maven-Module | 4 | 5 |
| Container im Stack | 15 | 17 (Modul + eigene Postgres) |
| Postgres-Instanzen | 6 | 7 — eigene Engine-DB ist Pflicht (GG-ARCH-PROCESS-0001) |
| Host-Ports | … 8127, 5527 | + 8128 (nur Loopback), 5528 |
| Keycloak-Clients | 2 | 3 |
| Spring-Boot-Module (Ausnahme von Zwirn-ADR-0007) | 1 | 2 — dieselbe benannte Ausnahme, nicht eine zweite Begründung |
Was offen bleibt
| Punkt | Warum |
|---|---|
| Keine Aufgaben-Oberfläche | Braucht den Process-Hub-Aggregator, den es weder in Zwirn noch hier gibt. Die Task-API existiert (§4a) — nur kein Bildschirm davor. Bedienbar per REST-Client, nicht per Klick |
Kein Registry-Eintrag (hub.process-services) |
Es gibt keine Registry. Pflicht 2 aus GG-ARCH-PROCESS-0001 ist damit in diesem Produkt nicht erfüllbar, nicht „übersprungen" |
| Statische Mandantenliste im Nachzug-Job | Eine neue Praxis braucht einen Eintrag in TXM_FEEDBACK_PROCESS_TENANT_KEYS, sonst läuft für sie nur der UI-Trigger — ohne Absicherung. Bewusst kein Rundruf bei der Authority im Minutentakt für einen Nachhol-Job |
Der set-status-Grant hängt am Demo-Seed |
Er wird von boot/CapabilityRegistrar.seedDemoGrants() gelegt, also nur bei TXM_DEMO_GRANTS=true und nur für den Demo-Mandanten — dieselbe Konstruktion wie beim SERVICE-Grant des Testmanagement-Service. Für eine echte Praxis ist er von Hand zu vergeben. Fehlt er, bleibt der Report auf OPEN und der ServiceTask scheitert sichtbar; nichts geht still verloren |
flowable.database-schema-update: true |
Die Engine legt ihr Schema selbst an. Für einen echten Betrieb durch eine verwaltete Migration ersetzen — derselbe offene Punkt wie beim Audit-Service |
| Kein nativer Bau | Bewusst JVM, benannte Ausnahme von GG-DEVOPS-0003 — dieselbe Begründung wie beim Cockpit (Flowable/Spring, Reflection) |
Kein Prozess-Steckbrief ({prozessname}-metadata.yaml, GG-ARCH-PROCESS-0004) |
Kein Big-Bang-Rollout vorgesehen; der Prozess reicht die drei BUSINESS_OBJECT_*-Variablen nach dem Altverhalten durch |
Verweise
- DOT-ADR-0030 — das gebaute Vorbild
../../../zwirn/guidelines/architecture/process/microprocess-architecture.md(GG-ARCH-PROCESS-0001), geändert durch DOT-ADR-0081 am 2026-08-12../../../zwirn/guidelines/architecture/process/bpmn-modeling.md(GG-ARCH-PROCESS-0002)../../../zwirn/samples/sample-process-flowable— die aktuelle Referenzimplementierung- ADR-0005 — die Authority, gegen die dieses Modul prüft
audit-feedback-processhub-integration.md— die Entscheidung von 2026-08-09, die hier fortgeschrieben (nicht aufgehoben) wird