Skip to content

Latest commit

 

History

History
406 lines (319 loc) · 50.9 KB

roadmap.md

File metadata and controls

406 lines (319 loc) · 50.9 KB

NodePilot Roadmap

Führendes Dokument für „was wird gebaut". Was hier nicht steht, ist kein Vorhaben — auch dann nicht, wenn es irgendwo als Idee, Plan oder Follow-up auftaucht.

Stand: 2026-08-22. Konsolidiert am 2026-08-03 aus dem damaligen docs/backlog.md, docs/ai-feature-ideas.md, docs/db-schema-audit-followups.md, den „Noch offen"-Abschnitten in docs/performance-improvements.md und docs/alerting.md, den offenen GitHub-Issues sowie der Session-Memory. Der Status der R1-Posten ist am 2026-08-22 gegen main verifiziert — was unten als erledigt markiert ist, liegt im Code. Am 2026-09-16 kamen die offenen Restposten aus dem Repository-Audit (August/September 2026) hinzu, der bis dahin als audit.md im Repo-Root lag; jeder Posten ist vorher am Code gegengeprüft.

Spielregeln

  • R1 — gesetzt. Wird gebaut, Reihenfolge unten ist die empfohlene.
  • R2 — steht auf der Roadmap, startet aber erst bei einer benannten Auslösebedingung. Kein Datum, kein „irgendwann" — jeder R2-Posten trägt seinen Trigger.
  • E — offene Entscheidung. Kann nicht gebaut werden, bevor sie getroffen ist.
  • Anhang — bewusst nicht auf der Roadmap, inkl. Begründung. Der Anhang ist ein Sperrvermerk: Er verhindert, dass verworfene oder gemessen widerlegte Ideen erneut aufschlagen.

Ein Posten wandert von R2 nach R1, wenn sein Trigger eintritt — nicht, weil er alt geworden ist.


R1 — Gesetzt

Allgemeine KI-Agenten und Teams (beschlossen 2026-09-18)

aiAgent und aiAgentTeam: allgemeine Agenten mit auswählbaren nativen Werkzeugen, PowerShell/CMD/Git Bash, MCP-Client, versionierten Skills, begrenzter Logsammlung und sichtbaren Teams mit begrenzten parallelen Aufträgen. Die Schleife liegt innerhalb des Steps; die Workflow- Engine bleibt bestehen. Veröffentlichung autorisiert konfigurierte autonome Aktionen; Chat-Freigaben bleiben unverändert. ADR 0016 und Betriebs-/Konfigurationsreferenz sind verbindlich. Entwicklung in drei Abschnitten, eine vollständige Veröffentlichung. ADR 0017 ergänzt parallele Delegationsbündel und Live-Teamhinweise; verschachtelte Teams und Wiederaufnahme nach Neustart bleiben ausgeschlossen. Umgesetzt und auf main (AiAgentActivity, AiAgentTeamActivity).

28 Posten in acht Wellen. Die Wellenreihenfolge ist bewusst: erst wird ehrlich, was gerade unehrlich ist, dann wird gebaut.

Welle 1 — Sicherheit

Detail zu den Findings liegt bewusst nicht in diesem öffentlichen Repo, sondern im Audit-Protokoll vom 2026-07-26 (Session-Memory security-audit-2026-07-26-open-items). Hier steht nur, was geändert wird.

# Posten Inhalt
1 Lokale Skript-Ausführung härten Entschieden (2026-08-15) Operator ist bewusst ein vertrauenswürdiger Automation-Author. Lokale Activities dürfen unter der NodePilot-Service-Identität laufen; Folder-RBAC ist keine Code-Sandbox. README und CLAUDE.md dokumentieren diese Trust Boundary. Ein Engine:RequireTargetMachineForScripts-Flag ist daher nicht vorgesehen.
2 SSRF-Guard: Adressabdeckung vervollständigen Erledigt (2026-08-18) Behoben und als M-34 in security-findings.md registriert, mit Regressionstest in beide Richtungen.
3 Passwortpolicy für Break-Glass-Konten Die Mindestlänge für explizit als Break-Glass markierte Konten anheben und eine Komplexitätsregel ergänzen.
4 restApi bekommt ein First-Class-Credential-Feld Ein credentialId-Feld analog zu den Remote-Activities, damit Auth-Daten denselben Weg nehmen wie überall sonst statt über frei befüllbare Header. Backend + Designer-Konfiguration.
5 Audit-Nachlauf: die verbleibenden Dimensionen Der Audit vom 2026-07-26 hat nicht alle Bereiche erreicht. Teilweise erledigt (2026-08-09): ein Nachlauf-Audit über deploy/, IDOR/Folder-Scoping, Alerting und die anonymen Endpunkte hat fünf Befunde geliefert, die alle behoben und in security-findings.md registriert sind — H-18, M-31, M-32, M-33, L-17. Der Rest der Fläche steht noch aus; der Umfang wird im Audit-Protokoll geführt, nicht hier.

Welle 2 — Hygiene

Ein Tag Arbeit. Danach lügt keine Quelle mehr — das ist Voraussetzung dafür, dass diese Roadmap trägt.

# Posten Inhalt
6 Stale Branches löschen Erledigt (verifiziert 2026-08-22) Die sieben Vor-Migrations-Branches, die inhaltlich hinter main lagen, existieren nicht mehr; git branch --merged main ist leer. Was lokal verbleibt, sind unmerged Arbeitszweige, keine Altlasten.
7 Status-Drift in der Session-Memory korrigieren Rund ein Dutzend Memory-Einträge behaupteten „ungemergt/geparkt" für Arbeit, die längst in main ist. Erledigt im Zuge dieser Roadmap-Erstellung — der Posten bleibt als Erinnerung stehen, dass Memory-Status regelmäßig gegen den Code zu prüfen ist.
8 docs/backlog.md aufgelöst Erledigt (2026-07-27, PR #81) Enthielt Ideen mit ✅-SHIPPED-Markierung neben echten Vorhaben. Inhalt ist in diese Datei überführt, die Datei ist entfernt, README zeigt hierher.
9 Offene Issues abräumen Erledigt (2026-08-09) #76 (Row-Serialization über DbAdminSecretColumns), #77 (Regressionstest für den Heatmap-Sortierfix) und #79 (premiumCanvas-Beschreibung mit Canvas-Hintergrund) sind geschlossen; der Issue-Tracker ist leer.
10 Dependabot einrichten Erledigt (2026-08-07, PR #126) .github/dependabot.yml für vier Ökosysteme (nuget + 3× npm), Minor/Patch je Ökosystem gruppiert, Majors bewusst einzeln — Semantik und PR-Budget stehen in CLAUDE.md. Ohne das lief der Versions-Drift auf, daher kam der 10.0.0/10.0.5/10.0.10-Split.
11 Doc-Sync AI-Chat / Knowledge Erledigt (2026-08-22) Aus dem AI-Chat-PR bewusst ausgeklammert, inzwischen auf allen vier Flächen nachgezogen: CLAUDE.md (Endpunkt + Wissensquellen), docs/ai-features.md, docs-ui content/{de,en}/ai-features.md und — als letztes Stück — das README-Highlight für den globalen AI-Chat.

Welle 3 — Eigene Konventionen einhalten

Keine Features, sondern Verstöße gegen Regeln, die im Repo bereits verbindlich stehen.

# Posten Inhalt
12 np alerting enable|disable + MCP-Tools CLAUDE.md: „Jeder neue API-Endpoint braucht beide Clients." Die Endpunkte POST /api/alerting/rules/{id}/enable|disable existieren. Die CLI erreicht den Zustand heute nur indirekt über np alerting update --enabled|--disabled; dedizierte enable|disable-Befehle und das MCP-Tool fehlen.
13 np-Befehlsgruppe für Custom Activities Gleiche Regel, gleiche Lücke. Der Controller ist da, die CLI-Gruppe fehlt.
14 Settings-UI: Alerting-Schwellwerte + vollständige Retention (a) Alerting:LongRunningSeconds / QueuedLongSeconds haben keine Schema-Section — der Code kommentiert wörtlich „Operator lowers … in the Settings UI", die es nicht gibt. Neue Section Alerting, hot-reloadable. (b) RetentionOptions hat fünf Sweeper, RetentionSettingsDto nur drei — Notifications und SupportEvents sind per UI nicht tunbar, ebenso die AuditLog-Verify-Parameter. (c) Schwellwert-Vereinheitlichung erledigt (#310): DashboardStats.LongRunningCount nutzt Alerting:LongRunningSeconds. Die Settings-UI bleibt priorisiert: Live-Ops und Dashboard machen die Wirkung sichtbar, aber der Operator kann den Wert weiterhin nur über die Konfiguration ändern. (a) und (b) bleiben offen.
15 Config-Panes internationalisieren Erledigt Alle zuvor genannten Komponenten unter components/designer/properties/ laufen über useTranslation; es sind keine hart verdrahteten deutschen UI-Texte mehr übrig.

Muster für Punkt 14: SettingsSchema.cs → DTO unter Api/Dtos/Settings/ → Adapter in SettingsSections.cs → Frontend-Section unter pages/settings/ + Tab in SystemSettingsPage.tsx → i18n DE+EN → Tests. AdminSettingsFrontendSyncTests erzwingt die Parität.

Welle 4 — Produktnutzen

# Posten Inhalt
16 Alert → Workflow (Auto-Remediation) Dritter Route-Target-Typ „Workflow" im NotificationDispatcher, kein neuer ITriggerSource. Wegen At-least-once-Delivery braucht die Route einen idempotenten Dispatch-Key (siehe ADR 0011).
17 Starter-Presets + „Beispiele importieren" Verifizierter Befund: NodePilot hat keinen Seeding-, Template- oder Galerie-Mechanismus. Einziger Ingest-Weg ist POST /api/workflows/import. Die scripts/*.json nutzen durchgängig targetMachineId: "localhost" und sind damit umgebungsunabhängig direkt seedbar. Zehn Presets mit aufsteigender Lernkurve sind entworfen (siehe unten). Liefermechanismus: Button auf der Workflows-Seite, kein First-Run-Seeding — die DB soll sich nicht ungefragt füllen.
18 Mail-Trigger (IMAP/EWS/Graph) Der SCOrch-Klassiker „Monitor Email" und die direkte Gegenrichtung zu emailNotification. Filter auf Absender/Betreff/Anhang, Anhänge als {{manual.attachmentPath}}. Größte Erwartungshaltung aus dem Ex-SCOrch-Umfeld.
19 ChatOps-Rezept dokumentieren Teams/Slack-Auslöser laufen heute schon über webhookTrigger + fieldMappings. Ein dokumentiertes Rezept deckt ~80 % des Bedarfs ohne eine Zeile Code. Ein Nachmittag.
20 Alerting-Regel-Vorlagen (Katalog) Detailplan: alerting-rule-templates-plan.md. Dieselbe Idee wie Posten 17, für Alerting: 30 kuratierte Custom-Regeln liegen heute nur in der Dev-DB, scripts/seed-custom-alert-rules.ps1 kennt davon 17, ist nicht idempotent und verdrahtet eine private Mail-Adresse. Künftig ein eingebetteter Katalog in NodePilot.Core (Muster ActivityConfigReference) + Dialog auf /alerts — kein Boot-Seeding, Bestandsinstallationen bleiben unberührt. Vorlagen sind zweisprachig (DE/EN, angelegt in der UI-Sprache), kommen ohne Route und immer deaktiviert; der Empfänger entsteht beim Aktivieren. Vier Vorarbeiten, die alle schon verifiziert sind: (a) neue Spalte NotificationRule.SourceTemplateId als Herkunfts-ID — namensbasierte Erkennung überlebt weder Umbenennen noch Sprachwechsel; SystemPresetId wird nicht überladen (dokumentiert System-only). (b) Routenlose Entwürfe zulassen, exakt wie die System-Policies es schon tun (SystemAlertingController 292-295 + Enable-Gate 135-136) — dazu gehören die drei Folgestellen: PreviewRule ruft TryBuildDraft mit hartem isEnabled: true, und results.All(…) ist über der leeren Routenliste true, wodurch test-fire heute Erfolg meldet, ohne etwas gesendet zu haben. (c) Client-Validierung nachziehen: AlertingRuleEditor.formValid verlangt Routen unbedingt, und toggleMutation hat kein onError — ein Enable-400 verpufft spurlos. (d) Batch-Vertrag validate-then-mutate: Sprache, unbekannte und doppelte Ids → 400 vor jeder Mutation; danach bewusst Teilergebnis nur für die konkurrierende Namenskollision, mit Detached je Fehlschlag, weil NotificationRuleStore.CreateAsync pro Regel speichert und ein DbUpdateException sonst den Tracker für den Rest des Batches vergiftet. Guard-Test mit Positiv- und Negativ-Fixture je Vorlage sowie einer Allowlist-Prüfung der Filterfelder gegen NotificationContext.ToFieldMap() — ein getipptes folderpath ergäbe sonst eine Regel, die korrekt aussieht und nie greift. Ersetzt scripts/seed-custom-alert-rules.ps1 (wird gelöscht). Der eigentliche Aufwand ist nicht das Gerüst, sondern die 30 Regelkörper samt englischer Texte — die bekommen einen eigenen PR ohne Controller-Rauschen, damit der Review die Frage stellen kann, die zählt. Fünf PRs.

Die zehn Presets (Posten 17), aufsteigende Lernkurve:

  1. Hello NodePilot — manualTrigger→log→runScript→returnData (Databus, Trigger-only-Roots)
  2. Disk Space Check — scheduleTrigger→runScript→decision→junction (param.*-Capture, Edge-Conditions)
  3. Log-Rotation & Archiv — folderOperation→forEach→fileHash→zipOperation→fileOperation
  4. API Health Check mit Alarm — restApi(+retry)→jsonQuery→decision→emailNotification
  5. Webhook-Empfänger — webhookTrigger(fieldMappings)→decision→runScript
  6. Dienst-Selbstheilung — serviceManagement→decision→start→waitForCondition→emailNotification
  7. Sub-Workflow-Paar (zwei Workflows) — startWorkflow + Child-returnData → Contract-Derivation
  8. Fehlerbehandlung & Kompensation — .failed-Edges, retry, exit N vs. successExitCodes
  9. Inventar-Report — parallele Zweige→junction→textFileEdit→emailNotification (Ahnen-Scope)
  10. Ordner-Watcher — fileWatcherTrigger→fileHash→decision→zip/move (Idempotenz per Hash)

Abdeckung: 21 von 35 Katalog-Typen + alle fünf ControlFlow-Nodes. Bewusst draußen (invasiv oder umgebungsabhängig, gehören in die Test-Suite unter scripts/test-suite/, siehe docs/workflow-tests.md): registryOperation, powerManagement, scheduledTask, startProgram, xmlQuery, generateText, sql, eventLogTrigger. Optional und gated: AI-Log-Triage (llmQuery, braucht Llm:Enabled), DB-Wächter (databaseTrigger). Leitplanken: localhost statt GUID, Sticky-Notes als Inline-Doku, externe Trigger kommen disabled an und werden per Publish oder Enable scharfgeschaltet — beide setzen PublishedByUserId, wenn die Spalte leer ist. Ein Workflow, der ohne einen dieser Wege auf IsEnabled=true kommt, endet in missing_effective_principal.

Welle 5 — KI-Ausbaustufe 1

Die drei Posten gehören zusammen und sollten als Block gebaut werden. Sie halten sich an die gemeinsamen Leitplanken in ai-feature-ideas.md.

# Posten Inhalt
21 Execution Incident Copilot „Fehler mit KI analysieren" an fehlgeschlagenen Executions (Liste, Operations-Ansicht, History-Tab). Erklärt beobachtete Fehler, trennt Evidenz von Vermutung, nennt nächste Prüfungen. Read-only, Folder-Leserecht, ausschließlich redigierte Daten, kein Retry, kein Publish.
22 KI-Reparatur für Lint und Publish „Mit KI beheben" übergibt ausgewählte deterministische Findings an den Workflow-Assistenten; Ergebnis ist ein Diff, der vollständig oder selektiv übernommen wird. Übernehmen erfordert Admin/Operator und den eigenen Edit-Lock; Stale-Hash-Schutz, Secret-Merge und Save-/Publish-Flow bleiben verbindlich.
23 AI-Chat Run-Card Tool propose_workflow_run führt nichts aus — es löst nur auf: Workflow via WorkflowNameResolver (exact-case → case-insensitive → 409 bei Mehrdeutigkeit, nicht die LLM raten lassen), Parameter gegen GET /{id}/contract, Zielmaschinen. Der Chat rendert eine Karte mit „Ausführen"-Button; erst der User-Klick ruft POST /execute — der User ist der Principal, nicht „die KI". Fehlende Pflichtparameter → Rückfrage statt erfundenem Default. Die Bestätigung ist eine harte Sicherheitsgrenze: Der Chat liest Doku, DB und Execution-Logs — jeder dieser Kanäle wäre sonst ein Prompt-Injection-Vektor. Zusätzlich Toggle AiKnowledge:AllowExecution (default false), Admin/Operator-Gating, eigener Audit-Code.

Welle 6 — Engine- und Designer-Nutzen

# Posten Inhalt
24 Rerun-from-Step (Hot Resume) „Rerun from here" an einem fehlgeschlagenen Step: frische Execution, vorbelegt mit den Outputs aller Upstream-Steps des Originallaufs. Spart das Neu-Ausführen der ersten 46 von 50 Steps. Drei gekoppelte Änderungen, jede mit Risiko — Detail unten. Aufwand ~1,5–2 Tage mit ordentlicher Testabdeckung.
25 Workflow-Tags / Labels Farbkodierte Tags am Workflow (prod, qa, incident-response); Dashboard, Quick-Switcher und Executions-Liste filtern danach, Pills überall neben dem Namen. ~1 Tag (Migration + UI). Bestes Aufwand/Wirkung-Verhältnis im Designer-Bestand.

Detail zu Posten 24 — die drei gekoppelten Änderungen:

  1. Engine-Signatur — WorkflowEngine.ExecuteAsync braucht ein optionales Dictionary<string, ActivityResult>? prefilledResults. Die Scheduling-Schleife muss results und completed damit vorbelegen, damit der Scheduler sie überspringt und beim ersten ungecachten Nachfahren einsetzt. Hot Path — braucht sorgfältige Tests für die waitAny/waitAll/junction-Fälle.
  2. Output-Parameter — StepExecution persistiert OutputParameters bereits redigiert in der nullable Spalte OutputParametersJson. Ein Rerun muss {{step.param.hostName}}-Referenzen des Originallaufs daraus wiederherstellen; fehlen sie, lösen sie zu Leerstrings auf und brechen Downstream-Logik still.
  3. API + UI — POST /api/executions/{id}/rerun-from/{stepId}: Originallauf laden, prefilledResults bauen (per BFS auf dem Graphen stepId und dessen Downstream ausschließen), ExecuteAsync rufen. UI: Kontextmenü-Eintrag in der History-Step-Liste.

Dateien: WorkflowEngine.cs (Signatur + Scheduler-Init), Core/Models/StepExecution.cs, Data/Migrations/, Api/Controllers/ExecutionsController.cs, components/designer/ExecutionPanel.tsx (HistoryRow-Kontextmenü).

Welle 7 — Guardrail

# Posten Inhalt
26 CI-Matrix: Migrationen gegen echte Container fahren Top-Empfehlung des DB-Schema-Audits. Fresh-Install und Upgrade-Pfad gegen echte PostgreSQL- und SQL-Server-Container ausführen, nicht nur GenerateScript. Die vorhandenen MigrationDriftTests generieren nur Skripte und fangen daher keine reinen Laufzeitfehler — z. B. ein ungeschütztes DropTable auf einer divergierten DB. Schließt eine ganze Fehlerklasse.

Welle 8 — Auslieferung

# Posten Inhalt
27 GUI-Setup für die Server-Installation Erledigt (2026-08-03, PR #108) Gebaut und unter deploy/server/ ausgeliefert: NodePilotServer.iss + Build-ServerInstaller.ps1, Adapter deploy/Invoke-NodePilotSetup.ps1 + deploy/SetupContract.ps1, deploy/Preflight.ps1, Tests deploy/Test-SetupAdapter.ps1 + deploy/Test-DeploymentTemplates.ps1. Der folgende Entwurf bleibt als Sperrvermerk stehen, weil seine Entscheidungen weiter gelten. — NodePilot-Server-Setup-<version>.exe (Inno Setup 6) als zweiter Weg zur selben Installation; der ZIP-Weg bleibt unverändert die Referenz. Der Pascal-Layer bleibt dünn — Seiten und Payload, keine Installationslogik: der Wizard schreibt eine ACL-geschützte Antwortdatei und ruft über Invoke-NodePilotSetup.ps1 das unveränderte Install-NodePilot.ps1. Nötig, weil -PostgresPassword ein [SecureString] ist und über powershell.exe -File gar nicht übergeben werden kann; /SILENT /ANSWERFILE= deckt damit zugleich SCCM/GPO ab. Größter Gewinn ist nicht die Oberfläche, sondern der Wegfall der Vertrauenszeremonie: ein Asset statt fünf, kein manueller Thumbprint-Abgleich, kein LocalMachine\Root-Eingriff von Hand. Readiness-Seite über die seiteneffektfreie Preflight.ps1 (Posten aus derselben Welle), zwei Opt-in-Aktionen (SQL-Login+DB nur mit ausreichenden Rechten, sonst nur DDL-Ausgabe; Labor-Zertifikat mit Warnung), ASP.NET-Core-Runtime als offizieller Microsoft-Installer im Payload (SHA512 gegen Release-Metadaten und eingecheckten Pin, Authenticode auf Microsoft, Standalone-Runtime statt Hosting Bundle). Re-Run erkennt die Installation über HKLM\SOFTWARE\NodePilot\Server und fährt Update-Semantik; /FULLREINSTALL erzwingt Neuaufsetzen inklusive neuem External-Trigger-API-Key (bestätigungspflichtig). Die Deinstallation entfernt die Datenbank nicht und bekommt dafür auch keine Option — der Installer legt sie nicht an, sie ist separat bereitgestellt und wird im Cluster von beiden Knoten geteilt. Einzige Frage ist das Datenverzeichnis, Default überall „behalten". Testfläche: Test-SetupAdapter.ps1 (Verhalten) plus mutationsgeprüfte Verträge in Test-DeploymentTemplates.ps1; der Pascal-Anteil bleibt bewusst minimal und ist samt manueller Smoke-Matrix als Testlücke in deploy/server/README.md dokumentiert.
28 Schlüsselfertige Erstinstallation (ohne Token-Eingabe) Erledigt (2026-08-05, PR #114) Gebaut: ProvisioningSeeder (Api/Security), NodePilot:BootstrapAdminUsername im Token-Einlösepfad, Answer-File-Gruppen bootstrap/seed in deploy/SetupContract.ps1. Der folgende Entwurf bleibt als Sperrvermerk stehen, weil seine Entscheidungen weiter gelten. — Ein unbeaufsichtigter Rollout endete bisher mit einer Instanz, die niemand benutzen kann: das Setup-Token müsste ein Mensch in die Anmeldemaske tippen. Zwei sich ausschließende Wege, ohne zusätzlichen Schalter — welcher greift, entscheidet allein, ob die Instanz danach Benutzer hat. (a) Answer-File-Gruppe bootstrap: das Setup löst das Token selbst ein und legt die erzeugten Zugangsdaten ACL-geschützt ab (SYSTEM + Administratoren, ACL vor Inhalt). Kennwort pro Maschine zufällig, kein adminPassword-Schlüssel — ein fester Wert wäre über die ganze Flotte gleich und würde gescannt statt geraten, auf einem Produkt, das PowerShell auf allen verwalteten Maschinen ausführt. NodePilot:BootstrapAdminUsername wird mitgesetzt, damit ein abgefangenes Token nur genau dieses Konto anlegen kann. (b) Answer-File-Gruppe seed: ProvisioningSeeder spielt beim ersten Start ein .npbackup ein — Benutzer, Workflows, Maschinen, Credentials und Settings —, bevor irgendetwas die Benutzertabelle liest, sodass gar kein Token entsteht. Möglich, weil die Admin-Sperre am Controller sitzt und nicht am BackupRestoreService; der Fall „leere Zieldatenbank" ist dort bereits behandelt und verlangt einen Break-Glass-Admin im eingespielten Satz — genau die Bedingung, an der später das Einschalten von LDAP/SSO hängt. Zwei Regeln machen den Seed dauerhaft konfigurierbar: er füllt nur eine leere Instanz (nie Migration), und er fail-closed — falsche Passphrase oder kaputte Datei lassen den Dienst nicht starten, statt eine scheinbar provisionierte, in Wahrheit leere Instanz zu hinterlassen. Die Passphrase geht in den Environment-Wert des Dienstschlüssels, nie in die JSON. Feste Default-Zugangsdaten wurden erwogen und verworfen. Im Lab in fünf Silent-Szenarien verifiziert.

R2 — Auf der Roadmap, trigger-gated

Jeder Posten trägt seine Auslösebedingung. Ohne Trigger wird nicht gestartet.

Daten & Performance

Posten Auslöser
F3 — SupportEvents-Freitextsuche (Full-Scan auf ungeindextem Message/UserName) Diagnose-Query p95 > ~500 ms oder SupportEvents > ~5 Mio. Zeilen im Retention-Fenster, und die Suche wird real genutzt. Messquery + providerspezifischer Fix: db-schema-audit-followups.md.
F7 — vermutlich ungenutzte AuditLog-Composites idx_scan = 0 über ≥30 Tage echten Traffic für einen konkreten Composite.
Per-Endpoint-Lese-Budgets Eine langsame, aber lebende Datenbank lässt interaktive Reads noch bis zu 120 s warten; Ausfall und Retry-Verstärkung sind bereits durch ADR 0011 abgedeckt.
Workflow-Liste über 500 Einträge — GET /api/workflows endet bewusst bei 500 ohne Fortsetzung (DoS-Guard, gepinnt durch GetAll_CapsResultSetAt500_EvenWhenMoreWorkflowsExist); UI-Suche und Ordnerfilter arbeiten lokal, MCP meldet totalAvailable Eine Installation mit mehr als 500 zugänglichen Workflows, z. B. ein entsprechend großer SCOrch-Export. Dann als Feature über SPA, CLI und MCP bauen — Vorbild ist die Executions-Pagination. Bis dahin verschwinden darüber nur die am längsten unberührten Workflows aus Listen und Pickern; Deep-Links und /export bleiben. Kleiner Zwischenschritt ohne Auslöser: im MCP totalAvailable durch ein truncated-Flag ersetzen.
F9 — GUID-PK-Fragmentierung (nur SQL Server) Nur auf einem SQL-Server-Deployment mit gemessener Fragmentierung > ~30 % trotz regelmäßigem Rebuild. Auf dem Default-PostgreSQL irrelevant.
Vier kleine Backend-Perf-Items (ExecuteDeleteAsync in der Execution-Retention, geteilte JsonSerializerOptions, LoggerMessage.DefineScope im StepRunner, inkrementeller HubRevocationSweeper) Alle Größe S–M und sauber, aber ohne gemessenen Schmerz. Guter Füllblock, wenn Kapazität übrig ist.
manualChunks in vite.config.ts · Locales getrennt laden (DE+EN ≈ 385 KB roh liegen beide im Eager-Pfad) Sobald NodePilot außerhalb des LAN erreichbar gemacht wird oder Initial-Load über VPN gemeldet wird. Der Rest dieses Postens ist erledigt: Route-Level React.lazy deckt 16 von 20 Seiten ab, Material Symbols kommen im Build gar nicht mehr vor, und ECharts ist am 2026-09-11 aus dem Boot-Chunk gezogen worden (Eager-Pfad 591 → 386 KB gzip, siehe performance-improvements.md). Beim Locale-Posten zuerst den Startup-Wasserfall bewerten — ein asynchroner i18n-Init muss vor dem ersten Render awaited werden.
React Compiler aktivieren (babel-plugin-react-compiler) Wenn wieder breit an Designer-Renderpfaden gearbeitet wird. Heute läuft nur die Lint-Hälfte: eslint-plugin-react-hooks v7 mit flat.recommended, während vite.config.ts ein blankes react() fährt und das Babel-Plugin nicht installiert ist — es gibt also keine Auto-Memoisierung. Der Code ist dadurch weitgehend compiler-sauber (Ausnahme: react-hooks/set-state-in-effect ist bewusst abgeschaltet), aber jede Memo-Grenze ist Handarbeit. Braucht die volle Frontend- und E2E-Suite, und löst die algorithmischen Posten ohnehin nicht.
SignalR-Batch-Coalescing (N Step-Events pro Execution zu einem Delta verdichten) Wenn das Concurrency-Plateau über die aktuellen ~600 in-flight Steps hinaus gehoben werden soll. Der einzige verbliebene Weg dorthin.
Backend-Mini-Benchmark (BenchmarkDotNet) Wenn Perf-Claims belegt werden müssen. Nicht präventiv.

Voraussetzung für F3/F7/F9 gemeinsam: pg_stat_statements bzw. Query Store aktivieren und ≥30 Tage repräsentativen Produktions-Traffic sammeln. Ohne echte Statistik ist jede dieser Änderungen Spekulation.

Trigger & Integration

Posten Auslöser
Execution-Event-Trigger (Workflow A endet → Workflow B, ohne dass A B kennt) Erster konkreter Verkettungs-Bedarf. Braucht Zyklen-/Tiefen-Guard analog startWorkflow (maxCallDepth 10).
databaseTrigger von Poll auf Push (Postgres LISTEN/NOTIFY, SQL Server Change Tracking) Wenn Polling-Latenz oder -Last real stört. Aufwertung eines bestehenden Typs, kein neuer Trigger.
WMI-Event-Subscription (__InstanceCreationEvent per WQL über WinRM, echtes Push) Kundenbedarf. Der anspruchsvollste Trigger, aber mit dem größten „kann sonst keiner"-Faktor.
Schwellwert-/Performance-Trigger („CPU > 90 % für 10 min", mit Hysterese) Kundenbedarf und vorherige Abgrenzung gegen die ADR-0008-System-Policies — die decken konzeptionell Ähnliches ab.
AD/LDAP-Change-Trigger (DirSync/uSNChanged) · Message-Queue (RabbitMQ/Service Bus/Kafka) · SNMP-Trap-Receiver Jeweils erster konkreter Kundenbedarf.

Pflicht-Gotchas für jeden neuen Trigger (gelten unabhängig vom Typ):

  • Erweiterungspunkt ist src/NodePilot.Scheduler/ITriggerSource.cs. Mechanisch billig — teuer sind die Nebenflächen: ActivityCatalog.cs + Frontend-Spiegel activityCatalog.generated.ts (Parity-Test!), Config-Komponente unter properties/triggers/, i18n DE/EN, Docs, Tests.
  • Trigger-Sources dürfen nicht in den Root-DI-Container lecken (siehe Commit 34f7874b).
  • Im HA-Passive-Knoten darf nichts doppelt feuern → externe Trigger leader-gaten.
  • Trigger-Daten landen als manual.* (+ param.* des Trigger-Nodes) — kein trigger.*-Namespace.
  • Trigger-Läufe brauchen Workflow.PublishedByUserId. Publish, Import, Duplicate, Restore und /enable setzen die Spalte, wenn sie leer ist (/enable überschreibt nie einen bestehenden Publisher). Nicht abgedeckt bleiben ein Workflow, der ohne einen dieser Wege auf IsEnabled=true kommt (First-Boot-Provisioning → missing_effective_principal), und ein deaktivierter Publisher (effective_principal_inactive); beide brauchen einmal Publish.
  • Health beantworten. Der Orchestrator wertet es sequenziell für jeden Trigger im 5-s-Pass aus → reiner In-Memory-Read, kein I/O, kein Lock. Wer echtes I/O braucht, probt auf eigenem Timer und cached das Urteil. Konstant Healthy ist erlaubt, aber nur mit Kommentar, der die Lücke benennt (Muster: EventLogTriggerSource). Ohne das bleibt eine Quelle, die im Betrieb stirbt, für immer registriert und feuert nie wieder.
Trigger-gated Posten Auslöser
Echte Health für eventLogTrigger (eigene Probe-Schleife statt konstant Healthy) Erster gemeldeter Fall einer stillen toten eventLogTrigger-Subscription. EventLog hat keinen Fault-Kanal; die einzige Probe wäre RPC an den EventLog-Dienst.
Lese-API für Laufzeit-Trigger-State (GET /api/triggers/status + CLI + MCP) Operator will Live-Zustand inspizieren, ohne auf einen Alert zu warten. Die System-Policy trigger-unhealthy deckt „sag mir Bescheid" bereits ab; das hier wäre „lass mich stöbern".

Engine & Datenbus

Posten Auslöser
{{run.*}} — Metadaten des laufenden Workflows im Template (run.workflowName, run.executionId, ggf. run.workflowId/run.startedAt) Ein produktiv gehender SCOrch-Import, dessen Runbooks Policy.Name/Policy.PID benutzen — oder der erste eigene Bedarf, Lauf und Workflow in Text zu benennen (Ticket-Betreff, Log-Zeile, Korrelations-ID). Am Referenz-Export gemessen: 13 der 46 Warnungen sind genau diese beiden Felder.

Heute gibt es dafür nichts: VariableResolver kennt globals., manual. und StepPattern (vier Tails). __executionId/__workflowName existieren nur als Ausgaben eines Child-Laufs, den startWorkflow gestartet hat — von innen kommt ein Workflow nicht an seine eigenen Daten.

Gotcha, falls der Posten startet: ein neuer Namespace braucht ein eigenes Regex-Muster und wird von StepPattern prinzipiell nicht mitgetroffen — genau daran ist manual.NAME bis 1.2.7 still gescheitert (Platzhalter blieb stehen, der Step meldete Erfolg). Dazu Seeding in der Engine, TemplateGrammarParityTests, Designer-Variablenpicker, MCP-/AI-Analyzer und Doku.

KI

Posten Auslöser
Activity-Konfigurations-Copilot (Idee 3) Nach Abschluss von Welle 5. Beginnend mit den komplexesten Configs (Decision-Bäume, Cron, REST, SQL, JSONPath/XPath, Retry).
Workflow Generator v2 — geführter Entwurf mit Rückfragen, Graph-Vorschau, Lint (Idee 4) Wenn der One-shot-Flow als Engpass gemeldet wird. Heute funktioniert er.
Reliability-/Optimierungs-Review (5) · Custom-Activity-Copilot (6) · Testfall- und Mock-Generator (7) · Operator-Copilot und Schichtbriefing (8) Alle setzen auf 1–4 auf. Nach Welle 5 neu bewerten.
Sechs weitere Knowledge-Tools im globalen Chat (Globals, Maintenance-Windows, Custom Activities, Alerting-Regeln, Credentials, Users) Wurden bewusst zurückgestellt. Wiederaufnahme, wenn im Chat konkret danach gefragt wird. read_settings und die DB-/text2sql-Quelle sind bereits gebaut.

Beschreibung, Nutzerproblem und Sicherheitsgrenzen je Idee: ai-feature-ideas.md.

Alerting

Per-Rule Dedup-Key-Templates sind bereits implementiert (DedupKeyTemplate, in der UI „Gruppieren nach“); siehe Alerting.

Posten Auslöser
PagerDuty- / Opsgenie-Sinks Erster Kundenbedarf. Die Enum-Werte existieren bereits, es fehlen die Sinks — Validierung lehnt Kanäle ohne registrierten Sink heute korrekt ab.
„Resolved"-Recovery-Notifications für Signal-Events Wenn silent recovery als Mangel gemeldet wird.
Escalation-Policies Enterprise-Bedarf.
sourceKey auch für Execution-Events befüllen Konsistenz-Posten, mitnehmen wenn ohnehin am Dispatcher gearbeitet wird.

Designer

Posten Auslöser
Group/Subgraph collapsible — N Kind-Nodes zu einem Platzhalter falten, per Doppelklick zurück Workflows regelmäßig > 50 Nodes. ~2 Tage (React-Flow-interne Node- und Edge-Behandlung für den Collapsed-Zustand).
JSONPath-Picker im Output-Tab — Klick auf einen Wert → {{step.param.hosts[0].name}} mit Copy-Button Wenn JSON-Pfade als Fehlerquelle auffallen. ~1,5 Tage.
Machine-Online-Indikator pro Node (grün/rot, 5-min-Cache) Zusammen mit einem WinRM-Reachability-Poller bauen, nicht davor. ~1 Tag.
Side-by-Side Workflow-Diff (zwei Canvas, geänderte Nodes hervorgehoben) Wenn der bestehende Text-Diff bei workflow-förmigen Änderungen als unbrauchbar gemeldet wird. ~2 Tage.
Restliste aus dem Designer-Redesign: Skin-Block-Dedup in index.css, vollständige ModalShell-v2-Konsolidierung der acht Overlays, Checkbox→Switch-Sweep über die restlichen ~13 Config-Dateien, Palette-Drag-Ghost Als ein Sammelposten. Kosmetik mit e2e-Risiko — nur angehen, wenn ohnehin am Designer gearbeitet wird.

Enterprise

Posten Auslöser
RBAC Stufe B — WorkflowPermissions-Tabelle für Per-Resource-Sharing Konkreter Kundenwunsch. Stufe A (Folder-RBAC inkl. Group-SID-Integration) ist seit Mai 2026 in main.
Active/Active-Cluster Nur bei Last-Skalierungsbedarf. Für Verfügbarkeit reicht Active/Passive (RTO 40–60 s).
HashiCorp Vault Transit / KMIP / Cloud-KMS · HSM-gestützter AES-Key · Per-Row-Key-ID ISecretProtector ist die vorbereitete Naht — ein neuer Provider ist eine Klasse plus DI-Zeile. Kein Vorbau nötig, jederzeit nachrüstbar. Details: secrets-providers.md.
Windows-SSO: Restlücken bis zum Wegfall des „Preview"-Labels Kerberos ist am 2026-08-02 gegen echtes AD verifiziert (Server 2025, gMSA, domain-joined Client; 22 PASS — echtes HTTP/<host>-Ticket, transitive tokenGroups, LDAP↔Windows dieselbe UserId, JIT-Race, Gruppen-Gate). Harness + Evidenz: scripts/ad-sso-labtest/. Offen bleiben: HAProxy-Pfad, Multi-DC-Konsens (Lab hat 1 DC), NTLM-Negativtest (braucht einen SPN-freien DNS-Alias — der Lab-Versuch war unschlüssig), Session-Revocation bei Gruppenentzug, OIDC/SCIM und HA-Restart. Das Label fällt erst, wenn diese Punkte abgeräumt sind.

Qualität & Betrieb

Posten Auslöser
Backend-Line-Coverage 89 % → 90 % Die Ratsche steht in ci.yml auf 85 % Line / 70 % Branch. Anheben, wenn ohnehin breit getestet wird — kein Selbstzweck.
IWorkflowDefinitionMutator — ein Mutations-Pfad für Workflow-Definitionen Aus dem Audit 2026-08-15 (P1). Version, UpdatedAt, berechnete Metadaten, History und Trigger-Sync liegen heute mehrfach nebeneinander in Create/Publish/Duplicate/Restore. Auslöser: der nächste Bug, der nur einen dieser Pfade trifft.
Custom-Activity-Invarianten DB-seitig erzwingen Aus dem Audit 2026-08-15 (P2). Heute nur App-Level-ConcurrencyToken in CustomActivityDefinitionStore; DB-seitig fehlen IsConcurrencyToken() und Live-Key-Eindeutigkeit. Braucht EF-Migration + Parallel-Test mit zwei DbContexts. Auslöser: erste beobachtete Race in der Praxis.
Credential-Rotation an den WinRM-Pool koppeln Erledigt (2026-09-06) Aus dem Audit 2026-08-15 (P2). Der Pool-Key trägt einen Fingerprint aus Benutzername, Domäne und gespeichertem Passwort; ein bearbeitetes Credential bekommt sofort eine frische Session, Idle-Sessions unter dem alten Key laufen per TTL aus, ausgeliehene Sessions beenden ihren Step unter der Identität, mit der sie geöffnet wurden. Vorgezogen, weil die Idle-TTL bei jeder Wiederverwendung neu startete und eine alte Identität unter Dauerlast unbegrenzt weiterlief.
N+1 bei Notifications, Workflow-Statistiken und Ordner-Grants bündeln Aus den Audits 2026-08-15 (P2) und 2026-09-05 (M2). Ziel-Größenordnung laut Betreiber: ~100 parallele Läufe. Ordner-Grants: ResourceAuthorizationService.GetAncestryGrantsAsync fragt je unterschiedlichem Ordner einmal ab — nur für Non-Admins, durch das 500er-Cap begrenzt; Kandidat wäre der Folder-Tree-Endpoint, der mit der Gesamtordnerzahl skaliert. Auslöser: gemessene Latenz, nicht Verdacht — vorher docs/performance-improvements.md gegenlesen.
Step-Writes DB-seitig fencen (HA) Aus dem Audit 2026-08-26. Execution-Terminalwrites laufen über CAS, Step-Writes über normales SaveChanges; ClusterFencingHost cancelt kooperativ, und verwaiste Running-Steps räumt ExecutionStateLifecycle.CancelOrphanedStepsAsync weg. Auslöser: ein beobachteter Step-Write eines abgesetzten Leaders nach einem Failover.
Audit- und SIEM-Zustellung über eine Outbox absichern Aus dem Audit 2026-08-26. AuditWriter bricht eine Mutation bei einem Schreibfehler bewusst nicht ab — eine erfolgreiche Mutation garantiert deshalb keinen Audit-Datensatz. Auslöser: eine Compliance-Anforderung, die einen lückenlosen Audit-Trail je Mutation verlangt.
Migrations-Identität von der Laufzeit-Identität trennen Aus dem Audit 2026-08-26. Die Laufzeit-Identität braucht heute db_owner auf der NodePilot-Datenbank, weil sie beim Boot migriert. Auslöser: ein Kunden-DBA verlangt die Trennung.
Strikte E2E-Fixture: ungemockte /api-Aufrufe lassen den Test scheitern Aus dem Audit 2026-09-05 (T1). e2e/fixtures/mockApi.ts beantwortet nicht gemockte Aufrufe mit 200 [] — dokumentierte Konvention (e2e/README.md). Die Lücke betrifft Read-only-Endpoints, deren Payload keine Spec prüft; Objekt-Endpoints scheitern mit [] sichtbar. Auslöser: ein Fehler, der durch die Catch-all-Antwort gerutscht ist. Vorher alle Specs auf beiläufige Aufrufe durchsehen.
Baum-Algorithmen der beiden Ordnerbäume einmal implementieren Aus dem Audit 2026-09-05. RecomputePathsRecursive, IsDescendant und DescendantMaxDepth stehen doppelt in SharedWorkflowFoldersController und GlobalVariableFolderStore. Getrennte Services wegen unterschiedlicher Autorisierung bleiben richtig, doppelte Algorithmen nicht. Auslöser: der nächste Bug, der nur eine der beiden Kopien trifft.
Prozessweiten Zustand der WorkflowEngine in einen DI-Singleton überführen Aus dem Audit 2026-09-05. Statische Dictionaries (laufende Executions, Cancel-Gründe, User-Zähler, Debug-Handles) zwingen die Engine-Tests in die serielle SerialEngineTestCollection. Auslöser: die Engine-Suite wird in CI zum Zeitfaktor, oder ein Test flakt am geteilten Zustand.
Kleine Restposten aus dem Audit 2026-09-05 (a) GlobalVariableFoldersController.MapError: _ => throw ex setzt den Stacktrace neu, und InvalidOperationException wird pauschal als Eingabefehler (400) gewertet. (b) Switcher: ActivityLogger hält die Historie unbegrenzt im Speicher, MainWindowViewModel baut die Liste bei jedem Eintrag komplett neu — Ringpuffer und inkrementelle Aktualisierung. (c) Erledigt: Toc.tsx liest die Überschriften bei Sprachwechsel neu, und resolveDocLink in DocMarkdown.tsx behält das Fragment. Auslöser: mitnehmen, wenn ohnehin an der jeweiligen Datei gearbeitet wird.
Reale Integrationsgrenzen (Container-Migrationen, Browser↔echte API, Electron-Smoke) Aus dem Audit 2026-08-15 (P2). Alle bestehenden Suiten sind hermetisch gemockt. Größter Einzelposten der Liste; sinnvoll nur stückweise.
WorkflowEditorPage in Hooks zerlegen + Gzip-Budgets in CI Aus dem Audit 2026-08-15 (P2). Die Seite ist zuletzt weiter gewachsen. Monaco und ELK bleiben erlaubte Lazy-Ausnahmen. Auslöser: wenn der Initial-Chunk spürbar wird.
xUnit1051 abbauen, produktive Nullable-Warnungen als Fehler Aus dem Audit 2026-08-15 (P3). Bewusst schrittweise — als Big-Bang bricht es die halbe Suite auf einmal.
WorkflowEditorPage.test.tsx splitten (2166 Zeilen / ~108 Tests) Wenn die Frontend-Suite in CI zum Zeitfaktor wird. Die Flakiness selbst ist gefixt.
E2E-Spec für /metrics/:section Einzige Seite ohne Playwright-Spec. Mock-Smoke-testbar.
/operations (Live-Ops) über den Snapshot-Happy-Path hinaus RBAC-Cross-Checks, Drilldown und Health-Rail sind unasserted; SignalR-Live bleibt untestbar (404-Stub).
MCP Streamable-HTTP-Transport Bewusst als Erweiterungspunkt dokumentiert, nicht gebaut. Erster Bedarf nach nicht-stdio-Anbindung.
Least-Privilege-DB-Login statt lexikalischer Read-Only-Prüfung Deployment-Thema, kein Code-Thema. Die autoritative Lösung für den DbAdmin-Read-Pfad auf SQL Server/SQLite.

E — Offene Entscheidungen

Diese drei Punkte können nicht gebaut werden, solange die Entscheidung aussteht. Sie sind bewusst keine R2-Posten: Es fehlt nicht der Auslöser, es fehlt der Beschluss. Die Frage nach Downloads in der Desktop-App ist entschieden: gleiche Herkunft mit nativem Speichern-Dialog (PR #448).

Frage Kontext
SSH/Linux-Cross-Platform: neu aufsetzen oder streichen? Die frühere Implementierung ist nicht mehr im Repo — RemoteProtocol findet sich nirgends in NodePilot.Core, Branch und PR sind mit der Public-Repo-Migration verloren gegangen. Damals: ~11 000 Zeilen über 107 Dateien, Backend platform-aware (Bash-Adapter für runScript/File/Folder/Service/StartProgram), SSH.NET-Stack mit Host-Key-TOFU/Strict, gegen Debian-bookworm per Docker verifiziert. Die bekannte Lücke war ausschließlich UI-seitig: RunScriptConfig.tsx blieb PowerShell-zentriert. Als „pausiert" weiterzuführen ist die einzige Option, die eindeutig falsch ist.
Multi-DC: Quorum oder All-DC-Konsens? Heute strikter All-DC-Konsens, kein Failover: Ein DC down ⇒ externe Logins 503. Das ist per Unit-Test festgeschrieben und inzwischen ehrlich dokumentiert (früher stand fälschlich „Failover" da). Ob das so bleibt, ist ein Produktentscheid — kein Bug.
Fremde MCP-Server im AI-Chat nutzbar machen? Admin konfiguriert externe MCP-Server, der Chat darf deren read-only Tools aufrufen. Das ist der eigentliche Nutzen eines MCP-Clients im Backend — fremde Tools anbinden. Die Gegenrichtung (den Chat über nodepilot-mcp auf die eigenen Tools fahren) ist geprüft und verworfen: nodepilot-mcp authentifiziert sich mit einer DPAPI-Session, der Chat läuft pro Request unter dem ClaimsPrincipal des Aufrufers (Folder-RBAC, SQL nur für globale Admins) — ein Umleiten würde alle Tool-Calls unter eine Dienst-Identität legen; dazu Loopback-HTTP je Call und ~90 Schreib-Tools, die der Chat bewusst nicht hat. Die geteilte Analyse-Logik liegt stattdessen in NodePilot.Core (WorkflowAnalyzer, WorkflowDataBusAnalyzer), von beiden Flächen konsumiert. Offen für den Client-Fall: Trust-Modell gegen Prompt-Injection aus fremden Tool-Ergebnissen, RBAC-Gate, Prozess-Lifecycle.

Anhang — Bewusst nicht auf der Roadmap

Dieser Abschnitt ist ein Sperrvermerk. Er existiert, damit diese Ideen nicht alle paar Monate erneut aufschlagen und neu analysiert werden.

Gemessen widerlegt (Performance)

Alle folgenden Hebel wurden in echten 500-Parallel-Stress-Tests durchgemessen und sind aktiv schädlich. Belege, Zahlen und Begründungen: performance-improvements.md.

Idee Messergebnis
Concurrency-Caps hochziehen (MaxConcurrentSteps 600→1500, Runspace 768→1500) 42 % schlechter. Das System saturiert downstream der Engine — CIM-Provider serialisieren, die OS-Process-Spawn-Rate wird vom CSRSS gedrosselt.
Concurrency-Caps reduzieren (600→300) 9 % schlechter. 500 Workflows × 1 Step brauchen mindestens 500 Slots. Die Defaults liegen in beide Richtungen nahe am Optimum.
ChildSemaphore von 128 auf 600 heben 16 % schlechter. Verschiebt den Stau nur eine Schicht weiter.
DB-Pool aufbohren (800→1500) Null Effekt. Nur ~85 von 480 Pool-Slots sind unter Last belegt.
Eager RunspacePool-Pre-Warm + MinRunspaces 768 28 % Regression. OS-Thread-Kosten dominieren über Runspace-Init-Kosten; der Pool wächst unter Last ohnehin organisch.
VACUUM ANALYZE auf den Hot-Tabellen Null Effekt. Autovacuum hält n_dead_tup bereits nahe 0.
Batch-Writes für StepExecution-Persists über einen 50-ms-Channel Rechnerisch attraktiv, aber bricht die Live-Step-Anzeige (UI sieht Steps nicht in der DB, bevor der Batch flusht) und verkompliziert die Cancellation-Pfade. Architektur-Risiko zu hoch.

Regel daraus: Wenn der nächste „lass uns Pre-Warm bauen / die Caps hochziehen"-Vorschlag kommt, erst mit dem dokumentierten 500-Parallel-Test verifizieren, dann committen. Das bauchgesteuerte „mehr Slots muss schneller sein" ist hier konkret falsch.

Verworfene Produktentscheidungen

Idee Grund
Native Teams- / Slack-Sinks Vom Product Owner gestrichen. Der Bedarf wird über webhookTrigger + fieldMappings und generische Webhooks gedeckt (siehe R1-Posten 19).
Multi-Tenancy (RBAC Stufe C) 2026-05-15 explizit gestrichen — kein Konzern-Kundenbedarf in Sicht, der 6–8 Personentage rechtfertigt.
OIDC/Entra-ID als Roadmap-Posten Wurde inzwischen gebaut (release-gated, inkl. SCIM-Controller) — der alte „gestrichen"-Vermerk ist überholt.
Composite- / Sub-Workflow-Nodes bei Custom Activities Bei v1 bewusst verworfen: Custom Activities bleiben reine runScript-Presets, keine zweite Script-Engine.
secret-Input-Typ für Custom Activities Gestrichen — Secrets laufen über {{globals.X}} und Credentials.
R2 „nur manualTrigger ist als Child aufrufbar" Zugunsten von Modell C zurückgenommen: startWorkflow ruft jeden enabled Workflow, unabhängig vom Trigger-Typ. Bekannter, bewusst akzeptierter Edge-Case: Ein Child mit mehreren Triggern feuert beim Call alle als Roots.
Multi-tone Node-Icons 2026-07-08 verworfen — flache Silhouette + Glyph bleibt.
dotnet pack für CLI/MCP (global tools) Die Tools hängen transitiv an net10.0-windows; PackAsTool verbietet ein Platform-TFM (NETSDK1146). Echte Tool-Pakete bräuchten Multi-Targeting der ganzen Kette. 2026-08-02 nachgemessen: dotnet pack scheitert für beide Projekte reproduzierbar. Die Doku hatte bis dahin dotnet tool install -g als Installationsweg angegeben — also einen Weg, den es nie gab; sie zeigt jetzt auf dotnet publish + PATH. Die toten PackAsTool/ToolCommandName/PackageId-Properties sind aus beiden .csproj entfernt, damit die Behauptung nicht erneut entsteht.

Refactoring ohne Anlass

Idee Grund
WorkflowEngine aufspalten (~1500 LOC) Gut getestet, hohes Risiko bei der Aufspaltung, kein Schmerzpunkt. Ein detaillierter 8-PR-Schnittplan wurde vorgelegt und als technisch sauber bewertet — aber ohne Auslöser nicht durchgezogen. Wiederaufnahme nur, wenn ein neues Feature (Dynamic Sub-DAGs, Saga-Patterns, Engine-Plugins) am Monolithen scheitert.
Controller ausdünnen (WorkflowsController ~970 LOC, ExecutionsController ~660 LOC) Controller dürfen dick sein, solange die Logik testbar ist — sie ist es. Wiederaufnahme nur, wenn neue Endpunkte ohne Service-Schicht hässlich werden.
BackupRestoreService zerlegen (~1650 LOC) Aus dem Audit 2026-09-05. Der Restore ist korrekt und getestet. Wiederaufnahme, wenn ein neuer Backup-Bereich nicht mehr sauber hineinpasst — dann entlang eines Restore-Planers schneiden.
Install-NodePilot.ps1 zerlegen (~1700 LOC: ACLs, Zertifikatrechte, SCM-Anlage, Rollback) Aus dem Audit 2026-09-05. Durch Test-SetupAdapter.ps1 und Test-DeploymentTemplates.ps1 abgesichert. Wiederaufnahme, wenn eine Installer-Änderung an der Größe scheitert; die Schnittgrenze ist der vorhandene SetupContract.
WorkflowEditorPage Overlays-Host (B5) Erledigt (2026-10-16) Der Auslöser (über ~1700 LOC) war mit rund 2060 Zeilen eingetreten. Die Overlay-Zustände liegen in useEditorOverlays, Lock/Save/Publish in einem einmal berechneten EditorLifecycle (useEditorLifecycle); Edge-Detach, Tidy und Export sind eigene Hooks, New-Workflow-Pill und Simulations-Banner eigene Komponenten. EditorOverlays und EditorHeader bekommen gruppierte Objekte statt rund 90 bzw. 46 Einzel-Props. Die Page liegt bei rund 1490 Zeilen.

Extern blockiert

Nicht unser Problem, aber dokumentiert, damit es nicht erneut versucht wird:

  • Microsoft.OpenApi 3.x — Swashbuckle 10.2.3 verlangt die 2.x-Oberfläche (transitiver Floor 2.7.5). Pin auf 2.x (derzeit 2.12.2) bleibt nötig. Erst nach einem Swashbuckle-Major nachziehen.
  • TypeScript 7 — typescript-eslint bricht beim Laden hart ab; Support erst ab TS ≥ 7.1 geplant. Side-by-side TS 6+7 oder das Lint-Gate opfern wurden beide abgelehnt.
  • Spectre.Console 0.57.2 — Spectre.Console.Cli endet stabil bei 0.55.0 und pinnt den Core auf die eigene Version. Familie bewusst geschlossen auf 0.55.0.
  • WireMock.Net 2.16.0 — zieht Microsoft.OpenApi 3.10.2 transitiv nach und kollidiert mit dem 2.x-Floor oben (NU1605 beim Restore). Bleibt auf 2.15.0, zusammen mit dem OpenApi-Eintrag zu heben.
  • jsdom 30.1.0 — die vier BackupPage-Tests, die einen FormData-Body über msw posten, laufen in den Timeout; lokal gegen 30.1.0 reproduziert. Bleibt auf 30.0.x, bis msw nachzieht.
  • vitest 5 — @vitest/coverage-v8 pinnt seinen Peer auf die exakte vitest-Version, der Bump geht also nur als Familie. Probiert: vitest 5.0.1 fährt alle drei Roots grün (3.219 / 136 / 112) und das Coverage-Gate hält, aber im Gesamtlauf von nodepilot-ui fällt ExecutionsPage an einer Cross-File-Verunreinigung, die isoliert und unter vitest 4 nicht auftritt. Migration, kein Bump.

Kein Produkt-Scope

  • Codex-Plan-Review-Hook — Entwickler-Tooling (PreToolUse-Hook auf ExitPlanMode), gehört auf eine Tooling-Liste, nicht auf die Produkt-Roadmap.
  • OS-Tuning (MaxShellsPerUser, CSRSS-Desktop-Heap) — per Host zu setzen, nicht im Repo regelbar.
  • Workflow-Definitions-Rewrites (z. B. Get-CimInstance Win32_* durch gecachte Aufrufe ersetzen) — wäre die einzige Stellschraube mit zweistelligem Perf-Gewinn im Demo-Workload, ist aber workflow-spezifisch und außerhalb des Engine-Scopes. Eingangsthema für künftige Designer-Lints.

Verwandte Dokumente

Dokument Rolle
db-schema-audit-followups.md Messqueries, Schwellenwerte und Wiederaufnahmebedingungen für F3/F7/F9. Das Format dieses Dokuments ist die Vorlage für alle R2-Posten.
ai-feature-ideas.md Nutzerproblem, Funktionsbeschreibung und Sicherheitsgrenzen je KI-Idee. Priorisierung liegt hier in der Roadmap.
performance-improvements.md Umgesetzte Optimierungen, Tuning-Erkenntnisse und die Messprotokolle hinter dem Sperrvermerk oben.
security-findings.md Register behobener Findings mit Fix und Test. Kein Backlog.
alerting-rule-templates-plan.md Detailplan zu Posten 20: Katalog-Schema, Batch-Vertrag, routenlose Entwürfe, PR-Schnitt. Stand der Entscheidung, nicht des Codes.
alerting.md · custom-activities.md · enterprise-features.md Fachliche Tiefe zu den jeweiligen Bereichen.