# Implementierungsplan: Predictalytics auf Windows + Linux **Stand:** 2026-08-06 · Basis: [ANALYSE-Linux-Portierung.md](ANALYSE-Linux-Portierung.md) **Ziel:** Ein Code-Stand, der unter Windows und Linux identisch läuft — Bedienhülle in Avalonia, gesamte Solution auf .NET 10. --- ## 0. Drei Entscheidungen vorab — **alle getroffen (2026-08-06)** | # | Entscheidung | Ergebnis | |---|---|---| | 0.1 | EF Core | **Weg A** — TFM `net10.0`, EF Core `9.0.18` + Pomelo `9.0.0` | | 0.2 | UI-Framework | **Avalonia 12.1.1**, kein LiveCharts2 | | 0.3 | Linux-Ziel | **Beides** — Avalonia-GUI *und* Headless-Modus (AP 3.4 verbindlich) | ### 0.1 ✅ EF Core: Version 9 behalten, nicht auf 10 gehen — **entschieden: Weg A** Das ist der einzige Punkt, an dem „alles auf .NET 10" nicht sauber aufgeht. **Befund (verifiziert über nuget.org am 2026-08-06):** ``` Pomelo.EntityFrameworkCore.MySql → neueste stabile Version: 9.0.0 (2025-08-17) nuspec: targetFramework net8.0 Microsoft.EntityFrameworkCore.Relational [9.0.0, 9.0.999] ← harte Obergrenze ``` **Es gibt keinen Pomelo-Provider für EF Core 10.** Die Versionsspanne ist nach oben geschlossen; ein Upgrade auf EF Core 10 ist mit Pomelo technisch unmöglich. | Weg | Konsequenz | |---|---| | **A — empfohlen: TFM `net10.0`, EF Core `9.0.18`, Pomelo `9.0.0`** | Alle Projekte laufen auf .NET 10. EF-Core-9-Pakete (net8.0) laufen problemlos auf der .NET-10-Runtime. **Kein Provider-Wechsel, kein DB-Risiko.** Nur +1 EF-Major statt +2. | | **B — EF Core `10.0.10` + `MySql.EntityFrameworkCore 10.0.9` (Oracle)** | Erzwingt den Wechsel des Datenbankproviders. Andere SQL-Generierung, andere Migrations-Historie, abweichendes Verhalten bei Typ-Mapping und Bulk-Operationen — **gegen die Produktiv-DB.** Realistisch +3–5 PT plus Regressionsrisiko in 94 Infrastructure-Dateien und 30+ Migrationen. | **→ Gewählt: Weg A.** „Auf .NET 10 gleichbleiben" ist das eigentliche Ziel und wird vollständig erreicht — das Zielframework aller Projekte ist `net10.0`. Dass die EF-Core-*Bibliothek* bei 9.0.18 bleibt, ist ein Implementierungsdetail ohne Auswirkung auf Runtime, Sprachfeatures oder Toolchain. Sobald Pomelo einen EF-Core-10-Provider veröffentlicht, ist der Nachzug ein Einzeiler. Weg B entfällt; ein Providerwechsel gegen die Produktiv-DB findet nicht statt. ### 0.2 Avalonia 12.1.1 — entschieden ✅ ``` Avalonia 12.1.1 (2026-07-29), lib-TFMs: net8.0, net10.0 ← natives .NET 10 Avalonia.Desktop 12.1.1 · Avalonia.Themes.Fluent 12.1.1 · Avalonia.Controls.DataGrid 12.1.2 ``` Eine Einschränkung: **`Avalonia.Diagnostics` liegt noch bei 11.3.18**, es gibt keine 12.x-Version. Das ist das DevTools-Inspektorfenster (F12) — reines Entwicklerwerkzeug, für die Laufzeit irrelevant. Falls beim Layouten vermisst: temporär eine 11.3.18-Referenz nur in der Debug-Konfiguration, oder ohne auskommen. **LiveCharts2 wird nicht benötigt.** Die Bedienhülle enthält kein einziges Diagramm; alle Charts liegen in der Web-UI (Chart.js). Keine Chart-Bibliothek im Shell-Projekt. ### 0.3 ✅ Wo läuft die Linux-Instanz? — **entschieden: beides** Eine Avalonia-GUI braucht unter Linux eine Desktop-Umgebung: X11 (bzw. Wayland über XWayland), dazu `libx11`, `libice`, `libsm`, `libfontconfig1` und installierte Fonts. Auf einem **headless Server ohne Desktop startet sie nicht.** **→ Gewählt: beides in einem Binary.** Die Avalonia-Shell für Desktop-Betrieb (Windows und Linux), zusätzlich ein Schalter `--headless` für Server ohne Desktop-Session. Damit ist **AP 3.4 verbindlicher Bestandteil**, nicht optional. Nach der Hosting-Extraktion (Phase 2) kostet er ca. 0,5 PT, weil der gesamte Kern dann bereits GUI-frei ist. Konsequenzen, die dadurch fix werden: * Der Lizenzschlüssel muss **auch ohne Dialog** beziehbar sein (Konfiguration / Umgebungsvariable) — koppelt an die laufende HardwareID-Arbeit. * Das Deployment liefert zwei Betriebsarten: Desktop-Start und systemd/Container. * Phase 6 verifiziert beide Modi. --- ## 1. Zielarchitektur Der entscheidende Umbau ist nicht die GUI, sondern das **Herausziehen eines plattformneutralen Hosting-Kerns**. Heute liegen ~870 portable Zeilen (EmbeddedWebServer, Watchdog, Serilog-Setup, Settings) im windows-gebundenen Projekt fest. Danach ist die GUI nur noch eine dünne Schale darüber — und der Headless-Modus fällt fast von selbst ab. ``` src/ Predictalytics.Domain net10.0 unverändert Predictalytics.Application net10.0 unverändert Predictalytics.Infrastructure net10.0 unverändert Predictalytics.Api net10.0 unverändert Predictalytics.Worker net10.0 unverändert Predictalytics.Hosting NEU net10.0 ← plattformneutraler Kern Predictalytics.Shell NEU net10.0 ← Avalonia-Bedienhülle (Win + Linux) Predictalytics.Application.Tests net10.0 net10.0 statt bisher net10.0 (bleibt) Predictalytics.WinFormsHost net10.0-windows ← Übergang, s. 6.2 ``` **`Predictalytics.Hosting`** (neu, GUI-frei) nimmt auf: | Herkunft | Ziel | |---|---| | `WinFormsHost/Services/EmbeddedWebServer.cs` (365 Z.) | `Hosting/PredictalyticsHost.cs` — unverändert bis auf Binding + wwwroot | | `WinFormsHost/AppSettings.cs` (137 Z.) | `Hosting/PredictalyticsOptions.cs` — ohne `[Category]`/`[Editor]`-Attribute | | `WinFormsHost/Services/WatchdogHeartbeatService.cs` (141 Z.) | 1:1, ist bereits portabel | | `WinFormsHost/Program.cs:33-148` (Serilog-Setup) | `Hosting/LoggingSetup.cs` — 1:1, ist bereits portabel | | `WinFormsHost/Services/LicenseGuard.cs` (102 Z.) | `Hosting/LicenseGuard.cs` — ohne WinForms-Timer/Dialog | | `Infrastructure/Logging/RichTextBoxSink.cs` | umbenennen → `DelegateSink.cs` (ist schon portabel) | **`Predictalytics.Shell`** (neu, Avalonia) enthält nur noch View + ViewModel. --- ## 2. Phase 0 — Vorarbeiten ✅ **erledigt (2026-08-06)** | # | Aufgabe | Ergebnis | |---|---|---| | 0.1 | `InvariantCulture` ergänzen | ✅ `LimitlessProvider.cs:270` (Volume) | | 0.1b | **Nachtrag:** 13 × lokalzeit-verzerrendes `DateTime.TryParse` | ✅ neuer Helfer `Infrastructure/Helpers/DateParsing.cs`, alle Fundstellen umgestellt | | 0.2 | Eingecheckte Logdatei entfernen | ✅ nicht nötig — Datei ist **nicht** getrackt, `.gitignore:14 logs/` greift bereits | | 0.3 | `j:\`-Absolutpfad-Fallback entfernen | ✅ `EmbeddedWebServer.cs` | | 0.4 | Branch anlegen | ✅ `feature/linux-net10` | **Abnahme erreicht:** `dotnet build` → 0 Fehler, 10 Warnungen (alle vorbestehend). `dotnet test` → **100 bestanden, 0 Fehler, 1 übersprungen**. ### Nachtrag 0.1b — Umfang und offener Punkt `DateTime.TryParse("2026-08-06T12:00:00Z")` ohne `DateTimeStyles` liefert `Kind=Local` und rechnet in Lokalzeit um. Betroffen waren in beiden Providern `StartDate`, `EndDate`, `CreatedAt`, `ClosedAt`, `ExecutedAt`, `ExpirationDate` — während der Fallback derselben Zuweisungen (`DateTime.UtcNow`) korrektes UTC schrieb. Auf der Berliner Maschine also 1–2 h Versatz, auf einem Linux-Server mit `TZ=UTC` hingegen nicht: derselbe Code hätte plattformabhängig unterschiedliche Werte erzeugt. ✅ **Altdaten-Backfill: entfällt (Entscheidung 2026-08-06).** Der Fix wirkt nur für neue Schreibvorgänge; bereits gespeicherte Zeilen tragen den 1–2-h-Versatz weiter. Bewusst akzeptiert — die Daten verlieren mit steigendem Alter ohnehin an Relevanz, und die betroffenen Felder werden bei jedem Markt-Sync überschrieben. **Konsequenz für Phase 6:** Beim Vergleich Linux-Lauf ↔ Windows-Referenz (Prüfung 8.3) dürfen historische Zeilen nicht als Referenz dienen — nur frisch geschriebene Datensätze sind aussagekräftig. --- ## 3. Phase 1 — .NET 10 (1,5–2,5 PT) Bewusst **vor** dem Avalonia-Umbau: eine Baustelle nach der anderen, und das Shell-Projekt entsteht dann direkt auf dem Zielframework. ### AP 1.1 — Zentrale Frameworkverwaltung `Directory.Build.props` erweitern: ```xml net10.0 latest enable enable ``` `` aus allen `.csproj` entfernen (Ausnahme: WinFormsHost → `net10.0-windows`). Empfohlen: zugleich `Directory.Packages.props` mit `ManagePackageVersionsCentrally` einführen — bei 7+ Projekten mit überlappenden Paketen verhindert das künftige Versionsdrift. ### AP 1.2 — Paketmatrix | Paket | heute | Ziel | Anmerkung | |---|---|---|---| | `Microsoft.EntityFrameworkCore(.Relational/.Design)` | 8.0.11 | **9.0.18** | Obergrenze durch Pomelo | | `Pomelo.EntityFrameworkCore.MySql` | 8.0.2 | **9.0.0** | zieht MySqlConnector 2.4.0 | | `Microsoft.EntityFrameworkCore.InMemory/.Sqlite` (Tests) | 8.0.11 | **9.0.18** | muss zu EF Core passen | | `Swashbuckle.AspNetCore` | 6.9.0 | **10.2.3** | ⚠️ 6.x ist nicht ASP.NET-Core-10-tauglich | | `Serilog` | 4.2.0 | 4.4.0 | | | `Serilog.AspNetCore` | 8.0.3 | 10.0.0 | | | `Serilog.Sinks.File` | 6.0.0 | 7.0.0 | | | `Serilog.Extensions.Logging` / `.Sinks.Console` / `.Formatting.Compact` | 8.0.0 / 6.0.0 / 3.0.0 | jeweils aktuell | | | `Microsoft.Extensions.Http` / `.Hosting.Abstractions` / `.Logging.Abstractions` | 8.0.x | 10.0.x | | | `Microsoft.NET.Test.Sdk` / `xunit` / `Moq` / `coverlet` | aktuell | prüfen | | ### AP 1.3 — Erwartete Bruchstellen * **Swashbuckle 6.9 → 10.x** ist der wahrscheinlichste Reibungspunkt: geänderte API-Oberfläche, und ASP.NET Core 10 bringt mit `Microsoft.AspNetCore.OpenApi` eine eigene OpenAPI-Generierung mit. Betrifft `Api/ApiConfiguration.cs:19-20` und `WinFormsHost/Services/EmbeddedWebServer.cs:143-144,154-155`. *Alternative:* auf das eingebaute `AddOpenApi()`/`MapOpenApi()` umsteigen und Swashbuckle ganz ablösen — sauberer, aber die Swagger-UI müsste ersetzt werden (z. B. Scalar). * **EF Core 8 → 9:** überschaubar. Zu prüfen sind Migrationsverhalten und die `ExecuteSqlRawAsync`-Aufrufe in `EmbeddedWebServer.cs:56-100`. * `Predictalytics.Application.Tests` steht bereits auf `net10.0` — hier wird die Inkonsistenz gerade aufgelöst statt vergrößert. **Abnahme:** `dotnet build` + `dotnet test` grün; WinFormsHost startet, Worker laufen, Web-UI erreichbar; **Migrationen gegen die Dev-DB (`bergisnu_db0`) verifiziert, nicht gegen Prod.** --- ## 4. Phase 2 — Hosting-Kern extrahieren (1,5–2 PT) **Der Schlüsselschritt.** Reiner Verschiebe- und Entkopplungsvorgang, keine neue Logik. ### AP 2.1 — Projekt `Predictalytics.Hosting` anlegen `net10.0`, Referenzen auf `Api`, `Worker`, `Infrastructure`, `LicenseLabrador.Client`. ### AP 2.2 — Verschieben (Tabelle in Abschnitt 1) `EmbeddedWebServer` → `PredictalyticsHost`, Watchdog, Serilog-Setup, LicenseGuard. `RichTextBoxSink` → `DelegateSink` umbenennen (der Sink ist ein reiner `Action`-Delegat und war nie WinForms-abhängig). ### AP 2.3 — Konfiguration entkoppeln `AppSettings` → `PredictalyticsOptions` ohne `System.ComponentModel`-Attribute. Ladereihenfolge: `appsettings.json` → `appsettings.{Environment}.json` → Umgebungsvariablen (`PREDICTALYTICS_`) → `settings.json` im Benutzerprofil (Kompatibilität). Ablageort plattformabhängig über `Environment.SpecialFolder.ApplicationData` (Windows: `%APPDATA%`, Linux: `~/.config`) statt neben der Executable — das Installationsverzeichnis ist unter Linux üblicherweise nicht beschreibbar. ⚠️ Beim Umbau ist `SslMode = MySqlSslMode.None` (`AppSettings.cs:100`) fest verdrahtet. Da die Zugangsdaten künftig auch über Netz zu einer entfernten DB gehen, sollte das konfigurierbar und standardmäßig aktiv werden. ### AP 2.4 — Lifecycle GUI-frei machen * `System.Windows.Forms.Timer` → `PeriodicTimer` bzw. `IHostedService` (`MainForm.cs:71` DB-Größe, `LicenseGuard.cs:70` Revalidierung) * `Application.Exit()` → `IHostApplicationLifetime.StopApplication()` * `PredictalyticsHost` bekommt Events/Observables (`StatusChanged`, `DbSizeChanged`), an die sich später sowohl das Avalonia-ViewModel als auch der Headless-Modus hängen. ### AP 2.5 — wwwroot sauber deployen `FindWwwrootPath()` (`EmbeddedWebServer.cs:308-335`) mit der Verzeichnis-Heuristik ersatzlos streichen. Stattdessen in den Host-`.csproj`: ```xml ``` und im Code nur noch `Path.Combine(AppContext.BaseDirectory, "wwwroot")`. **Abnahme:** WinFormsHost benutzt nur noch `Predictalytics.Hosting` und enthält ausschließlich UI-Code. `Predictalytics.Hosting` baut ohne Windows-Referenzen. --- ## 5. Phase 3 — Avalonia-Bedienhülle (4–6 PT) ### AP 3.1 — Projekt `Predictalytics.Shell` ```xml ``` MVVM mit `CommunityToolkit.Mvvm` (Source-Generatoren) — bei ~15 Settings-Properties plus Status-Bindings spart das spürbar Boilerplate gegenüber handgeschriebenem `INotifyPropertyChanged`. ### AP 3.2 — Control-Mapping | WinForms (heute) | Avalonia | Aufwand | |---|---|---| | `MenuStrip` + 3 Menüs | `Menu` / `NativeMenuBar` | klein | | `ToolStrip` + 2 Buttons | `StackPanel` + `Button` | klein | | `TabControl` / `TabPage` | `TabControl` / `TabItem` | klein | | `StatusStrip` + 3 Labels | `DockPanel` unten + `TextBlock` | klein | | `MessageBox.Show` (7 Aufrufe) | eigener Dialog oder `MsBox.Avalonia` | klein | | **`RichTextBox` (farbiges Log)** | `ListBox`/`ItemsControl` über `ObservableCollection`, `DataTemplate` mit `Foreground`-Binding | **mittel** | | **`PropertyGrid`** | **kein Äquivalent in Avalonia Core** | **mittel–groß** | **Zum Log-View:** die bisherige Ringpuffer-Logik (`TerminalHelper.cs:26-31` — bei 500 Zeilen `rtb.Clear()`) wird zur `ObservableCollection` mit `RemoveAt(0)` — angenehmer, weil die Historie nicht mehr komplett verworfen wird. Marshalling über `Dispatcher.UIThread.Post` statt `Control.BeginInvoke`. Farbschema aus `TerminalHelper.cs:33-39` direkt übernehmbar. **Zum PropertyGrid** — das ist der einzige echte UI-Entwurfsaufwand. Drei Optionen: | Option | Bewertung | |---|---| | **Handgeschriebene Settings-View** (empfohlen) | 4 Gruppen / 15 Properties (Webserver 2, Egress 1, Watchdog 6, Database 4 + abgeleiteter ConnectionString). Als `Expander` je Gruppe mit `TextBox`/`CheckBox`/`NumericUpDown`. Volle Kontrolle über Passwortfelder (`PasswordChar`) und Mehrzeilen-Editor für Egress. ~1–1,5 PT. | | Drittanbieter `Avalonia.PropertyGrid` | Spart Zeit, bringt aber eine Abhängigkeit fragwürdiger Pflegelage und passt selten zum Fluent-Theme. | | Reflection-basierter Generator | Elegant, aber Overengineering für 15 Properties. | ### AP 3.3 — Plattformdetails * **Fixe Fenstergröße auflösen:** `MinimumSize`/`MaximumSize = 1886×1088` (`MainForm.Designer.cs:233-234`) ist bei abweichender DPI/Auflösung unter Linux untauglich. → skalierendes Grid-Layout, sinnvolles `MinWidth`/`MinHeight`. * **Schriftart:** `"Cascadia Code"` (`MainForm.cs:25`) existiert unter Linux meist nicht. → Fallback-Kette `Cascadia Code, DejaVu Sans Mono, monospace`. * **`Process.Start("explorer.exe", …)`** (3×, `MainForm.cs:192,207,209`) → ```csharp Process.Start(new ProcessStartInfo(target) { UseShellExecute = true }); ``` funktioniert auf Windows und Linux (dort über `xdg-open`). * **Lizenzdialog** (`LicenseDialog.cs`, 118 Z.) in Avalonia nachbauen: Label, `TextBox` (Großschreibung — `CharacterCasing.Upper` hat kein direktes Pendant, per `TextInput`-Handler oder Converter lösen), Status-Label, zwei Buttons. Der Aktivierungsablauf (`ValidateAsync` + `VerifyChecksum`) bleibt unverändert. *Hinweis: greift auf die HardwareID-Arbeit zu, die separat läuft.* ### AP 3.4 — Headless-Modus *(verbindlich, 0,5 PT — Entscheidung 0.3)* Schalter `--headless` in `Program.cs` der Shell: statt `BuildAvaloniaApp().StartWithClassicDesktopLifetime()` direkt `PredictalyticsHost` starten und auf `IHostApplicationLifetime` warten, Logs nur auf Console. Nach Phase 2 ist das reine Verdrahtung, weil der Kern bereits GUI-frei ist. Zwingend mitzudenken: * **Lizenz ohne Dialog:** Schlüssel aus `PREDICTALYTICS_LICENSE_KEY` bzw. Konfiguration; bei fehlender/ungültiger Lizenz sauberer Abbruch mit Exit-Code ≠ 0 und klarer Logzeile statt eines Dialogs, den niemand sieht. * **Keine Avalonia-Initialisierung** vor der Argumentauswertung — sonst versucht der Prozess auf einem Server ohne X11 zu starten und stirbt, bevor der Schalter greift. * SIGTERM sauber behandeln (`IHostApplicationLifetime`), damit systemd korrekt stoppt. **Abnahme:** Shell startet unter Windows **und** Linux-Desktop mit GUI; derselbe Build startet mit `--headless` auf einem Server ohne X11. In beiden Modi: Worker laufen, Web-UI erreichbar, Settings persistieren; im GUI-Modus zusätzlich farbiger Log-View und Lizenzdialog. --- ## 6. Phase 4 — Aufräumen und Absichern (1–1,5 PT) ### AP 4.1 — Kestrel-Binding und Absicherung ⚠️ `UseUrls($"http://localhost:{port}")` (`EmbeddedWebServer.cs:122`) konfigurierbar machen (`ASPNETCORE_URLS` bzw. Options), CORS-`AllowedOrigins` (heute fest `http://localhost:5000`) mitziehen. **Sobald nicht mehr nur auf `localhost` gelauscht wird, entfällt die bisher implizite Absicherung.** `MapPredictalyticsControlEndpoints()` (schreibende Operationen) und Swagger wären dann offen erreichbar. Das vorhandene `CanControl`/`AuthRequired`-Konzept (`Api/ApiConfiguration.cs:66`) muss davor scharf geschaltet werden. ### AP 4.2 — Globalisierung Im Host-Bootstrap `CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture` setzen, damit Zahlenformatierung unabhängig von der Systemlocale ist. `InvariantGlobalization=true` (spart `libicu` im Container) erst später erwägen — es ändert String-Vergleichssemantik und sollte nicht im selben Sprung passieren. ### AP 4.3 — Log-Verzeichnis `Path.Combine(AppContext.BaseDirectory, "logs")` (`Program.cs:33`) konfigurierbar machen. Default: Windows `%APPDATA%`, Linux `~/.local/share` bzw. `/var/log/predictalytics`. ### AP 4.4 — Solution und Repo-Kopplung * `Predictalytics.slnx`: WinFormsHost mit `Condition="$([MSBuild]::IsOSPlatform('Windows'))"` versehen, damit `dotnet build` der Gesamtsolution unter Linux durchläuft. * Die `..\..\..\..\LicenseLabrador\…`-Projektreferenz (4 Ebenen aufwärts) ist für CI und Container fragil → Git-Submodul oder NuGet-Paket. ### AP 4.5 — Egress-Kanäle prüfen ⚠️ `EgressPoolService.cs:70-88` bindet Sockets an eine Quell-IP. Unter Linux muss die IP tatsächlich auf einem Interface konfiguriert sein (sonst `EADDRNOTAVAIL`); Windows ist toleranter. Für nicht-lokale IPs bräuchte es `IP_FREEBIND`/`CAP_NET_ADMIN`. Nur relevant, falls Egress-Kanäle produktiv genutzt werden. Proxy-Kanäle sind unkritisch. --- ## 7. Phase 5 — Deployment und CI (1–2 PT) * `dotnet publish -r linux-x64` (und `win-x64`); bei self-contained an `--self-contained` + Trimming-Verhalten von Avalonia denken. * **Systemabhängigkeiten Linux (GUI):** `libx11-6`, `libice6`, `libsm6`, `libfontconfig1`, `libharfbuzz0b`, Monospace-Fonts (z. B. `fonts-dejavu`). **(GUI-Variante braucht eine Desktop-Session — siehe 0.3.)** * systemd-Unit bzw. Dockerfile (für den Headless-Modus); es existiert bereits ein `J:\Softwareprojekte\dockerCompose` — vermutlich anknüpfbar. * Reverse Proxy (nginx/Caddy) mit TLS vor Kestrel. * **Secrets:** DB-Passwort und Watchdog-API-Key liegen heute im Klartext in `settings.json`. Unter Linux → EnvironmentFile mit `0600` oder Secret-Store. *Randnotiz:* In `LicenseGuard.cs:16-17` stehen Basic-Auth-Zugangsdaten im Quelltext — unabhängig von dieser Portierung überdenkenswert, erst recht mit Blick auf Monetarisierung. * CI: Build + Test auf Linux **und** Windows. * `docs/BETRIEB-Watchdog-Lizenz.md` um den Linux-Betrieb ergänzen. --- ## 8. Phase 6 — Verifikation (2 PT) | # | Prüfung | |---|---| | 8.1 | Worker-Dauerlauf ≥ 24 h unter Linux gegen die **Dev-DB**, Logvergleich mit einem Windows-Lauf | | 8.2 | EF-Migrationen von Null gegen Dev-DB (Weg A: nur EF-9-Regression) | | 8.3 | Zahlenformatierung: Lauf unter `LANG=de_DE.UTF-8` — Volumina/Preise/PnL gegen Windows-Referenz | | 8.4 | Web-UI vollständig durchklicken (case-sensitive Assets, alle Endpunkte) | | 8.5 | Lizenz: Erstaktivierung, Neustart aus Cache, Offline-Gnadenfrist, Revalidierung | | 8.6 | Watchdog-Heartbeats erscheinen unter `watchdog.mhdf.de` | | 8.7 | Egress-Kanäle (falls genutzt) | | 8.8 | Shell unter Windows **und** Linux-Desktop: alle Buttons, Menüs, Settings-Persistenz | | 8.9 | Derselbe Build mit `--headless` auf einem Server ohne X11: Start, Lizenz aus Konfiguration, SIGTERM-Stopp | --- ## 9. Zeitplan | Phase | Inhalt | PT | |---|---|---| | 0 | Vorarbeiten | 0,5 | | 1 | .NET 10 + Paketmatrix | 1,5–2,5 | | 2 | Hosting-Kern extrahieren | 1,5–2 | | 3 | Avalonia-Shell **inkl. Headless-Modus (AP 3.4)** | 4,5–6,5 | | 4 | Aufräumen, Binding, Absicherung | 1–1,5 | | 5 | Deployment + CI (Desktop **und** systemd/Container) | 1,5–2,5 | | 6 | Verifikation (beide Betriebsarten) | 2 | | | **Summe** | **12,5–17,5 PT** | **≈ 2,5–3,5 Wochen fokussiert.** Phasen 0–2 sind risikoarm und liefern bereits Wert (aufgeräumte Architektur, aktuelles Framework). Phase 3 ist der Hauptaufwand. Die Phasen sind strikt sequenziell — jede baut auf der vorigen auf; einzig AP 5 (Deployment) kann parallel zu Phase 3 vorbereitet werden. --- ## 10. Risiken | Risiko | Eintritt | Wirkung | Gegenmaßnahme | |---|---|---|---| | **Pomelo hat keinen EF-Core-10-Provider** | **sicher** | blockiert EF Core 10 | ✅ gelöst durch Weg A: EF Core 9 auf net10.0 | | Swashbuckle 6.9 → 10.x mit Brüchen | wahrscheinlich | halber bis ganzer Tag | ggf. auf eingebautes `AddOpenApi()` wechseln | | PropertyGrid-Ersatz wird aufwendiger als geschätzt | mittel | +1 PT | handgeschriebene View, 15 Properties sind überschaubar | | Avalonia-GUI läuft auf dem Linux-Ziel nicht (kein Desktop) | sicher auf Servern | Shell unbrauchbar | ✅ abgedeckt durch AP 3.4 (verbindlich) | | `Avalonia.Diagnostics` fehlt für 12.x | sicher | nur Komfort | ohne DevTools arbeiten oder 11.3.18 in Debug | | **Lizenzaktivierung headless** | **sicher (AP 3.4)** | mittel | koppelt an laufende HardwareID-Arbeit — früh abstimmen | | Egress `SourceIp` unter Linux | niedrig | mittel | AP 4.5, früh testen | | Regression in der Datenschicht | niedrig (Weg A) | hoch | ausschließlich Dev-DB, nie Prod | --- ## 11. Nächster Schritt Alle Vorentscheidungen sind getroffen (Abschnitt 0). Umsetzung kann beginnen: 1. **Phase 0** — 0,5 PT, sofort, unabhängig vom Rest. Ergebnis ist ein aufgeräumter Ausgangspunkt auf `feature/linux-net10`. 2. **Phase 1** — .NET 10 + Paketmatrix. Hier zeigt sich früh, wie groß der Swashbuckle-Umbau tatsächlich ist. 3. Parallel dazu: **Lizenzaktivierung ohne Dialog** mit der laufenden HardwareID-Arbeit abstimmen — das ist der einzige Punkt im Plan, der von außen abhängt und dadurch spät teuer werden kann.