diff --git a/ausbauplan-v1.1-v1.3.md b/ausbauplan-v1.1-v1.3.md new file mode 100644 index 00000000..65930fb4 --- /dev/null +++ b/ausbauplan-v1.1-v1.3.md @@ -0,0 +1,277 @@ +# Ausbauplan „Master Linux Assistant (MLA)" — V1.1 bis V1.3 (GTK-Shell) + +> Repo: `Toqsick/master-linux-assistant` · Quelle: Planungssession 2026-10-18 (Brainstorming, read-only geprüfter Evidenz-Schnappschuss, Builder-Check) +> Zerlegung in Meilensteine und Issues: `meilensteine-v1.1-v1.3.md` · `issues-v1.1-v1.3.md` +> Vorgänger: `ausbauplan-v0.8.6-v1.0.md` (dessen Leitplanken, Nie-Liste und Security-Invarianten gelten unverändert weiter). + +> **Für ausführende Agenten:** Dies ist ein Master-Plan. Umgesetzt wird nie direkt aus ihm, sondern aus +> dem GitHub-Issue des Teilplans (Body in `issues-v1.1-v1.3.md`) und dem Kurzdesign/Teilplan, der beim +> Start entsteht. Jede ID (UX1, ST4, …) = ein Issue. Abschnitt „Für ausführende Agenten" unten beschreibt +> den Ablauf. + +## Kontext + +**Warum:** Nach V1.0 „Cockpit komplett" soll die Admin-Steuerzentrale Alltagskomfort bekommen: QoL und +Nice-to-haves, zugeschnitten auf das Referenzsystem (Zorin OS 18.1, GNOME 46, Wayland, NVIDIA-Laptop, +Docker-Stacks, Agenten, Gaming, mehrere Laufwerke), mit reifer UI/UX — und so festgehalten, dass autonome +Agenten die Pakete ohne Rückfragen ziehen können. + +**Stand 2026-10-18 (verifiziert):** +- Drei Stränge auf GitHub: V0.8.1–V0.8.5-Härtung (Milestones 1–5, PR #118 offen) · Ausbauplan + V0.8.6–V1.0 (Milestones 6–11, Issues #58–#88; FU1/#60 fertig) · MLA-Next-GTK-Track (Milestones 12–18, + Issues #90–#106; #93/#94/#95 fertig, PR #119 = A5 Monitor offen). 0.4.x = Flutter-vs-GTK-Entscheid (#106). +- Code: `prototype/gtk/mla_app.py` (GTK4/libadwaita-Scaffold mit vier Fixture-Modulen und rechter + Kontextleiste), Tokens `tokens.css`/`tokens-dark.css` mit Gate `tests/test_tokens.py`; + `packages/la_core` mit `ModuleRegistry`, `ProbeRegistry`, `Probe`/`ProbeResult`/`ProbeSeverity`, + `ProbeStatus`/`ProbeState`, `EventBus`, Parsern und der CLI `la_probe` (heute nur `--version`). + Registry und Probes werden von keinem Produktionsprozess konstruiert. IPC nur als Entwurf + (`docs/mla-next/IPC_CONTRACT.md`). Keine Notifications, keine User-Units, kein Autostart, keine + D-Bus-Dienste, keine GTK-l10n. + +**Entscheidungen (2026-10-18):** +- **Ziel-Shell = GTK (MLA-Next)**, nicht der Flutter-Hub. +- **Drei Wellen V1.1–V1.3**, je 9–12 Issues. +- **Parkliste (#88):** die drei günstigsten Mini-Varianten werden geöffnet — Wetter (Open-Meteo), + ntfy-Ziel im Wächter, Odysseus „Ungelesen: N". +- **Use-Case-Schwerpunkte:** NVIDIA/Wayland-Stabilität · Docker & Agenten-Betrieb · Gaming & Leistung · + Platten & Backups. +- **Verifikation bewusst schlank:** 3–5 Abnahme-Boxen je Issue, eine Gates-Zeile, kein + Reviewer-Handoff; die QA-Infrastruktur entsteht in V0.9.x–V1.0. + +## Prämisse und Rückfallregel + +V1.x setzt „V1.0 Cockpit komplett" **auf der GTK-Shell** voraus: Der Entscheid in MLA-Next 0.4.x (#106) +fällt für GTK, und die Teilpläne V0.9–V0.12 werden danach auf GTK umgeschrieben (nicht Teil dieses +Plans). Jedes V1.x-Issue ist auf sein **GTK-Delta** zum bestehenden Issue geschnitten und verweist mit +dem Einzeiler „#NN gilt für GTK sinngemäß: Kern = la_core/la_probe, Flutter-UI-Teil entfällt". + +**Rückfall:** Fällt der Entscheid gegen GTK, bleibt der la_core-Anteil jedes Issues gültig (Probes, Parser, +CLI, Zustandsdateien); nur die UI-Hälfte wird auf den Flutter-Hub umgeschrieben. Das steht hier einmal +und wird nicht in jedem Issue wiederholt. + +## Leitplanken V1.x (Ergänzung zu den Leitplanken des Ausbauplans V0.8.6–V1.0) + +1. **Logik-Heimat:** Parser und Probes → `packages/la_core` (Dart, Fixtures in `test/fixtures/*.txt`), + erreichbar über `la_probe --json ` (CLI-Zwischenstufe ohne Socket) und später über IPC + (FU4). Python (`prototype/gtk/`) übernimmt nur Darstellung, UI-Zustand und GNOME-Nähte (D-Bus, + Portale, Desktop-Dateien). Ausnahme: UX6 Wetter (reine Kachel, standardmäßig aus) bleibt Python. +2. **Zustandsdateien** (Schreiber → Leser, je mit `schema`-Feld, Schreiben atomar): + + | Datei | Schreiber | Leser | Inhalt | + |---|---|---|---| + | `$XDG_CONFIG_HOME/linux-assistant/config.json` | GTK (UX4) | la_probe, Flutter | Einstellungen; unbekannte Keys bleiben erhalten | + | `$XDG_STATE_HOME/linux-assistant/status.json` | la_probe (#66) | GTK | letzter Probe-Stand | + | `$XDG_STATE_HOME/linux-assistant/status-history.jsonl` | la_probe (LB4) | GTK | Übergänge, Ring 30 Tage | + | `$XDG_STATE_HOME/linux-assistant/state.json` | GTK (LB4) | la_probe | Bekannt/Snooze/Ruhezeiten/erledigt (Schema in la_core) | + | `$XDG_CONFIG_HOME/linux-assistant/gtk-state.json` | GTK (UX1) | GTK | Fenster, Layout, Onboarding-Marker | + +3. **Prozess-Modell:** kein Dauerprozess. Der IPC-Host (#100) ist ein Kindprozess der GTK-App + (`Gio.Subprocess`) und endet mit ihr; der Wächter bleibt systemd-User-Timer (#66); der + Shell-Suchanbieter (GN1) läuft als `IS_SERVICE` mit `hold()/release()` und Inaktivitäts-Timeout. +4. **App-ID ist Schritt 0** (GN4): Desktop-Datei, D-Bus-`.service` und die Aktion `app.open::` + gehören zu GN4; GN1/GN3 konsumieren sie. Vorläufig `dev.toqsick.MlaPrototype` für Tests, final nach #106. +5. **Fremdprogramme starten:** nie `bash -c`; detached über `Gio.AppInfo.create_from_commandline(...).launch()`, + `Gtk.FileLauncher`, `Gtk.UriLauncher`; Programmsuche über `GLib.find_program_in_path` mit fester + Kandidatenliste (Sicherheitsentscheid #110-C beachten). +6. **API-Floor:** GTK 4.14 / libadwaita 1.5 (Zorin 18): `Adw.PreferencesDialog/PreferencesPage/Breakpoint/ + Dialog/Banner/Toast/StatusPage/OverlaySplitView/AlertDialog`, `Gtk.ShortcutsWindow/FileLauncher/ + UriLauncher/Snapshot` ✓; `Adw.Spinner/ButtonRow/ShortcutsDialog` ✗. Python 3.12 + PyGObject, keine + pip-Pakete; Sparklines über `Gtk.Snapshot` (kein pycairo). Dart: kein neues pub-Paket; HTTP über + `dart:io` `HttpClient`. +7. **GTK-Teststrategie:** Modelle headless (`unittest`, Muster aus #119); Widget-Tests unter `xvfb-run` + mit apt `xvfb gir1.2-gtk-4.0 gir1.2-adw-1` (CI-Schritt kommt mit DS5); sonst `gate:manual`. + Gates-Zeile je Issue: `python3 -m unittest discover -s prototype/gtk/tests` · `cd packages/la_core && + dart test`; Flutter-Gates zusätzlich, wenn `config.json`-Kompatibilität berührt ist (UX4, TE4, LB4). +8. **l10n GTK (DS7):** gettext-Domain `linux-assistant`, `.po` de/en, Key-Konvention = ARB-Key; Probe- + `messageKey`s zeigen auf dieselben Keys. Satz-Abnahmen setzen DS7 voraus. +9. **Epic-Grenze:** DS = Tokens/Komponenten/A11y/l10n · UX = Navigation/Zustände/Einstellungen/Dashboard · + GN = GNOME-Nähte · ST = Stabilität/Diagnose · GA = Gaming/Leistung · LB/DI/SP/TE/WZ/FU wie bisher. +10. **`gate:manual`** = „Abnahme nur am lebenden GNOME möglich": GN1–GN4, DS5, DS6, UX7. Manuelle + Abnahme-Boxen sind als solche markiert. +11. **Öffentliches Repo:** keine privaten Unit-Namen, Ports, Hostnamen, Koordinaten oder Pfade in Code, + Doku und Issues — nur Muster (`-*`), Platzhalter (``), Demo-Werte und `config`-Keys. + Der Leak-Grep aus V0.8.5 gilt für alle neuen Dateien und Fixtures. +12. **Labels:** `shell:gtk` markiert das Ziel; `track:mla-next` wird nicht gesetzt (es beschreibt das + Experiment, V1.x ist Produktplanung). Neue Epics: `epic:ux`, `epic:gn`, `epic:ga`. + +## Evidenz-Schnappschuss (2026-10-18, read-only, generalisiert) + +| Befund | Quelle | führt zu | +|---|---|---| +| GNOME Shell 46, GTK 4.14, libadwaita 1.5, xdg-desktop-portal-gnome 46.x: Portale Background/Notification/Settings vorhanden, **kein GlobalShortcuts-Portal** | `busctl --user introspect`, Portal-Dateien | GN2 (gsettings bleibt), GN3, GN4, API-Floor | +| Shell-Suchanbieter werden nur aus System-Datadirs geladen; ein Custom-Keybinding für die App existiert | `/usr/share/gnome-shell/search-providers/`, gsettings | GN1 (Abnahme über .deb), GN2 | +| `journalctl -k -o json`, `--list-boots`, `tailscale status --json`, `docker ps`, `gamemoded -s`, `powerprofilesctl get`, `dkms status`, UDisks2-`SmartCriticalWarning` laufen als Nutzer | CLI/D-Bus | ST4, ST5, DI8, DI4, GA1, SP3 — alles ohne Root | +| Wiederholte Treiber-Livelocks unter Wayland, ein hängender Neustart; Runbook außerhalb der App; `/dev/watchdog` fehlt | Journal, Runbook | ST4 (Bereitschaft inkl. „nicht vorhanden"), ST5, DI8 (SSH-Rettungsweg) | +| Zwei Steam-Bibliotheken über `libraryfolders.vdf`; Flatpak-Steam nicht vorhanden | Dateisystem | GA2 (VDF-Parser statt Pfadraten) | +| `/etc/timeshift/timeshift.json` als Nutzer lesbar; der Zeitplan war unbemerkt regrediert | Dateisystem | SP3 (Soll-Abgleich) | +| Mehrere User-Timer für Backup, Heartbeat und Labs; keine fehlgeschlagene Unit zum Zeitpunkt der Aufnahme | `systemctl --user list-timers` | LB4/LB5, DI7 (Muster aus config) | +| `secret-tool`, `gapplication`, `gnome-terminal` vorhanden; `kgx`/`ptyxis` nicht | `which` | LB6/DI6 (Secret Service), GN2/GN3, WZ3 (Kandidatenreihenfolge) | +| PR #119 ändert `mla_app.py` und bringt `monitor_ui.py`/`monitor_model.py` | GitHub | DS5 (Extraktion), UX1 (Andockpunkte nach Merge) | +| Flutter-`ConfigHandler` ist eine flache Key/Value-Map; `custom_actions`/`exposure_baseline` existieren noch nicht | `lib/services/config_handler.dart`, #73, #80 | UX4 (unbekannte Keys erhalten, Schemata übernehmen) | + +## Ideenpool — Jobs auf dem Referenzsystem + +| Job | V1.x-Teilpläne | +|---|---| +| „Ist alles in Ordnung?" | FU4, LB4, LB5, SP3, ST4 | +| „Was ist passiert, was hat sich geändert?" | ST4, ST5, ST6 | +| „Meine Dienste im Griff" | DI4, DI5, DI6, DI7, DI8 | +| „Vor dem Spielen prüfen" | GA1, GA2 | +| „Schnell erledigen" | UX1, GN1, GN2, TE4, WZ3 | +| „Die Oberfläche trägt" | DS5, DS6, DS7, UX2, UX3, UX4, UX5, UX7, GN3, GN4, UX6 | + +- **Weglassen statt bauen:** kein GlobalShortcuts-Portal versprechen, kein Snapshot-Listing über den + Sicherheits-Report, kein eigener D-Bus-Stack (`busctl`/GI genügen), kein pycairo. +- **Integrieren statt nachbauen:** Terminal, Dateien, Obsidian, Grafana per Übergabe (WZ3); Steam, + Timeshift, Tailscale, Docker nur lesen und starten. +- **Bewusst draußen:** Tray, eingebettetes Terminal, Inline-Webview, Dauerprozess, Clipboard-Historie, + Proton-Vollclient, Gmail-Vollclient (Nie-Liste). + +## Release-Wellen + +| Release | Titel | Teilpläne | ⚡ sofort startbar | +|---|---|---|---| +| **V1.1** | Cockpit-QoL & UX | FU4, DS7, UX1, UX2, UX3, UX4, DS5, LB4, LB5, ST4, GA1, SP3 | DS7, UX1, UX2 (A), UX3 (A), UX4 (A), ST4, GA1, SP3 (A) | +| **V1.2** | Zorin/GNOME-Integration & Werkzeuge | GN4, GN1, GN2, GN3, DS6, WZ3, TE4, DI4, DI5, ST5, GA2 | GN4 (A), GN2 (A), WZ3, ST5 (A), GA2 | +| **V1.3** | Nice-to-haves & Parkliste | UX5, UX6, LB6, DI6, ST6, DI7, DI8, UX7, Abschluss | UX6, DI7, DI8 | + +Die Wellen sind thematisch geschnitten, nicht streng sequenziell: Ein ⚡-Teilplan aus V1.2 darf vor +einem blockierten V1.1-Teilplan starten. + +## Abhängigkeiten + +```text +#100 A4 IPC ─► FU4 ─► LB4, LB5, GN3, UX2(Verlauf) #102 Lagebild ─► LB4, LB5 +#66 Wächter ─► LB4, LB5, GN3, LB6, ST6 #96/#119 Monitor ─► DS5 ─► UX3(Galerie), DS6, UX5 +#97 A11y ─► DS6 #98/#99 ─► GN1(Notizen), TE4 DS7 ─► UX4(Sprache), TE4, UX7 +GN4 (App-ID, Schritt 0) ─► GN1, GN3; #105 Packaging ─► GN1, GN4(Teil B); #106 ─► finale App-ID +UX4 ─► GN2(Diagnose), UX5, UX7; UX1 ─► UX2(Zustand) +DI2/#68 ─► DI4; DI3/#69 + A8/#103 ─► DI5 ─► DI6; SP1/#83 ─► SP3(Prognose), LB5, ST6 +SI3/#82 + ST2/#77 ─► ST5(Teil B); ST4 ─► LB5, ST5, ST6, DI8(Verweis); #63/#101 ─► LB5, ST6 +``` + +Das Anlege-Skript setzt aus diesem Graphen die Zeilen „Blockiert durch / Blockiert" in den Issues. + +## Epics & Teilpläne + +Größen: S = höchstens eine Session · M = 2–3 Sessions · L = 4+ Sessions. Vollständige Bodies in +`issues-v1.1-v1.3.md`; hier die Kurzfassung. + +### Welle V1.1 — Cockpit-QoL & UX + +- **FU4 Generischer Probe-Vertrag über IPC** — Core · M · `la_probe --json ` als + CLI-Backend, `probe.list/get/watch` + `probe.changed` über IPC (registry-getrieben; `backup.*` wird + Spezialfall), GTK-`ProbeStore` mit Backends fixture → cli → ipc. Blockiert durch #100, #102. +- **DS7 l10n-Grundgerüst GTK** ⚡ — QoL · S · gettext-Domain, `.po` de/en, Key = ARB-Key, Ratchet-Test. +- **UX1 Navigation, Fensterzustand & Tastatur** ⚡ — QoL · M · `Adw.OverlaySplitView`, Rail unter 1000 px, + `gtk-state.json`, Strg+1…9/Esc/F5/Strg+B, `Gtk.ShortcutsWindow` aus zentraler Tabelle. +- **UX2 Kontext-Inspektor** ⚡ (A) — QoL · M · Panel mit Chip, Satz, Quelle/Messzeit/Einheit, Aktionen, + Verlauf (nach LB4); `DetailsProvider`-Protokoll je Modul. +- **UX3 Leer-, Lade-, Stale-, Fehlerzustände** ⚡ (A) — QoL · S · `states.py`, Hardcode-Gate auf alle `.py`. +- **UX4 Einstellungen als Seite** ⚡ (A) — QoL · M · `Adw.PreferencesPage` im Stack; Seiten Allgemein/ + Wächter/Watchlists/Exposition/Eigene Aktionen; `config.json` mit erhaltenen Fremd-Keys. +- **DS5 Komponenten-Katalog, Galerie & GTK-CI-Gate** — QoL · M · `widgets.py` aus `monitor_ui.py` + extrahiert, Galerie, xvfb-Schritt in CI. Blockiert durch #96. +- **LB4 Ampel-Feinschliff** — QoL · M · `state.json` (bekannt/snooze/Ruhezeiten), `status-history.jsonl`, + „Was tun?"-Satz. Blockiert durch #102, #66, FU4. +- **LB5 Tages-Check „Heute"** — QoL · M · Karte aus vorhandenen Probes inkl. Restore-Drill-Erinnerung; + Zeilen abhakbar. Blockiert durch #102, #66, FU4. +- **ST4 Grafik-Stabilitäts-Wache** ⚡ — QoL · M · Probe `gpu_stability` (Journal-Marker, unsaubere Boots, + known-bad-Treiber, Rettungs-Bereitschaft); GTK-Delta zu #76; geschwärzte Journal-Fixture. +- **GA1 Gaming-Vorflug** ⚡ — QoL · M · Probe `gaming_readiness` (GameMode, Profil, EPP, GPU, OC-Dienst, + PSI, Platz, Inhibitoren); eine bestätigte Aktion: `powerprofilesctl set`. +- **SP3 Speicher-Feinschliff** ⚡ (A) — QoL · M · Mount-Ampeln, Timeshift-Soll-Abgleich, SMART-Flag über + UDisks2 ohne Root; Prognose-Zeile nach #83. + +### Welle V1.2 — Zorin/GNOME-Integration & Werkzeuge + +- **GN4 App-ID, Desktop-Einbindung & Autostart** ⚡ (A) — QoL · S · gate:manual · Desktop-Datei mit + Actions, D-Bus-`.service`, `app.open`, symbolisches Icon, AppStream, XDG-Autostart-Schalter. +- **GN1 GNOME-Shell-Suchanbieter** — QoL · M · gate:manual · `SearchProvider2` über `do_dbus_register`, + Dienst-Modus mit Timeout, Notizen nur per Schalter. Blockiert durch GN4, #105. +- **GN2 Hotkey-Robustheit & Portal-Pfad** ⚡ (A) — QoL · S · gate:manual · `hotkey.py` (gsettings, + idempotent, verwaiste Einträge), Kommando `gapplication launch`, Portal-Erkennung; GTK-Delta zu #58-Q4. +- **GN3 Benachrichtigungen mit Aktionen & Deep-Links** — QoL · M · gate:manual · `systemd-run --user + notify-send --wait -A` → `gapplication action … open`, `Gio.Notification` in-app. Blockiert durch GN4, #66, FU4. +- **DS6 Tastatur, Accessibility & GNOME-Darstellung** — QoL · M · gate:manual · `accessible-description` + für Ampeln, Fokus-Walk unter xvfb, Reduce-Motion, Orca-Durchgang. Blockiert durch #97, DS5. +- **WZ3 Werkstatt-Übergaben & Dashboard-Links** ⚡ — QoL · S · Terminal/Dateien/Obsidian/Links über + `Gio.AppInfo`/`Gtk.*Launcher`, argv-Asserts; übernimmt den n2h-Link aus #85. +- **TE4 Palette-Aktionen & Schnellaktionen-Kacheln** — QoL · M · Provider für Module/Probe-Aktionen/ + Einstellungen/`custom_actions` (#73), `pinned`-Kacheln, Terminal-Modus mit argv. Blockiert durch #99, DS7. +- **DI4 Stack-Operationen & Container-Hygiene** — QoL · M · Compose-Operationen aus Labels, + Image-Alter, `docker system df` mit Vorschau. Blockiert durch #68. +- **DI5 Tunnel-Status & Gateway-Audit** — n2h · S · Probe `tunnel_status` aus config + `ss -ltnH`, + Audit read-only. Blockiert durch #69, #103. +- **ST5 Grafik-Setup-Check, Radar & GNOME-Erweiterungen** ⚡ (A) — QoL · M · modeset/Preserve/DKMS/ + Secure Boot, Kernel-Rollback-Bereitschaft, Erweiterungen; Radar (apt/Flatpak/Firmware) nach #82/#77. +- **GA2 Steam-Bibliotheken & Platzfresser** ⚡ — n2h · M · VDF-Parser in la_core, verwaiste Präfixe mit + Vorschau → `gio trash`. + +### Welle V1.3 — Nice-to-haves & Parkliste + +- **UX5 Dashboard anpassbar & Fokus-Modus** — n2h · M · Karten ein-/ausblenden, Fokus-Modus, Breakpoints. +- **UX6 Wetter-Kachel (Open-Meteo)** ⚡ — n2h · S · Parkliste 4.4; Ort manuell, Cache, Standard aus. +- **LB6 ntfy-Ziel im Wächter** — n2h · S · Parkliste Handy-Push; Token nur im Secret Service, nur + Übergänge nach Rot. Blockiert durch #66, LB4. +- **DI6 Odysseus „Ungelesen: N"** — n2h · S · Parkliste 4.11; generisches `GET … → {"unread": N}`. + Blockiert durch #69, DI5. +- **ST6 Wochenbericht** — n2h · M · `la_probe --weekly` + Timer, Schwärzung aus #78. Blockiert durch + #66, ST4, #82, #83, #63/#101. +- **DI7 Lab-Status: Container-Labs & VMs** ⚡ — n2h · S · Muster aus config, User-Timer, VBox optional. +- **DI8 Tailscale-Status** ⚡ — n2h · S · Zähler/Flags ohne IPs, SSH-Flag als Rettungsweg. +- **UX7 Erster Start & Onboarding** — QoL · S · gate:manual · `Adw.Dialog` einmalig. Blockiert durch UX4, GN2, DS7. +- **Abschluss V1.x** — roadmap · S · Parkliste, `features.csv`, Wiki, Doku-Altlasten. + +## Parkliste — Stand nach V1.x-Planung + +| Thema | Urteil 2026-09-23 | V1.x | +|---|---|---| +| 4.1 Hermes Web-UI/Client | geparkt | bleibt geparkt (Start-Knöpfe in #69) | +| 4.4 Wetter | Mini-Variante möglich | **UX6** (Go erteilt) | +| 4.6 Notizen-Ausbau | geparkt | bleibt geparkt (#98/#99 decken Suche/Capture) | +| 4.8 Dock / stufenlose Sidebar | Sidebar einklappbar = Mini | **UX1** (Sidebar einklappbar), Dock bleibt geparkt | +| 4.9 Terminal+ | Terminal-Übergabe = Mini | **WZ3/TE4** (Übergabe und argv-Ausführung) | +| 4.10 Proton-Hub | generischer VPN-Status = Mini | #67 (Interface-Probe), **DI8** nur Tailscale; Proton bleibt geparkt | +| 4.11 Gmail/Odysseus | „Ungelesen: N" = Mini | **DI6** (Go erteilt) | +| Handy-Push | ntfy-Ziel = Mini | **LB6** (Go erteilt) | + +## Für ausführende Agenten + +1. **Issue wählen:** `gh issue list --repo Toqsick/master-linux-assistant --milestone "" --label shell:gtk`; + ⚡-Issues zuerst (Zeile „Abhängigkeiten" im Body); bei Teil-⚡ nur den genannten Teil A. +2. **Vor dem Start:** Issue-Body lesen (nicht diesen Plan), Andockpunkte gegen den aktuellen `main` + prüfen (Dateien können sich seit dem Anlegen verschoben haben), bei Konflikt im Issue kommentieren. +3. **Einordnung:** bounded → Kurzdesign im Chat; architectural (FU4, GN1, GN3, LB4) → Spec, dann Teilplan. + Plan-Datei außerhalb des Repos. +4. **Branch** `feature/mla--` von `main`; Commits mit Conventional-Commit-Scope `mla` + und Co-Authored-By-Zeile des ausführenden Modells. +5. **Gates:** die Gates-Zeile des Issues plus die fünf bestehenden CI-Gates; Fixtures aus echten, + geschwärzten Ausgaben; Leak-Grep über Diff und Fixtures. +6. **Abnahme:** jede Box im Issue abhaken, manuelle Boxen als „für Basti offen" markieren; PR gegen `main` + mit Verweis auf das Issue. **Kein Push, Merge, Versions-Bump, Tag oder Policy-Update ohne Bastis OK.** +7. **Sicherheitsgrenzen (jedes Issue):** GTK-UI und Dart-Host nie als Root; keine Shell-Strings; + polkit-Grenze unberührt (keine dritte Action); Secrets nur im Secret Service, kein Klartext-Fallback; + keine privaten Hosts/Ports/Pfade im Code. + +## Risiken + +1. **#106 fällt gegen GTK** → Rückfallregel oben; la_core-Anteile bleiben, UI-Hälften werden umgeschrieben. +2. **FU4 kommt spät** → ⚡-Probes laufen über das `cli`-Backend (`la_probe --json`), kein Wartestau. +3. **App-ID wechselt nach #106** → alle Dateien beziehen die ID aus einer Konstante/Template (GN4). +4. **GNOME-Nähte sind nur manuell abnehmbar** → `gate:manual` ehrlich gesetzt; automatisierte Teile + (D-Bus-Tests unter `dbus-run-session`, Desktop-Datei-Validierung) bleiben in CI. +5. **Leaks im öffentlichen Repo** → Fixtures mit Streichliste (ST4), keine Unit-Namen/Koordinaten in + Issues, Grep-Gates in DI5/DI7/DI8/LB6/DI6. +6. **Scope-Drift** → jedes Issue nennt seinen Job und seinen Beleg; was keinen Beleg hat, bleibt auf der Parkliste. + +## Überführung in Repo & GitHub + +1. Repo-Fassung: dieser Plan, `meilensteine-v1.1-v1.3.md`, `issues-v1.1-v1.3.md`; Zeiger in + `docs/mla-next/MILESTONES.md`, `docs/mla-next/ISSUES.md`, `docs/wiki/Roadmap.md`. +2. Leak-Grep über alle neuen Dateien — muss leer sein. +3. GitHub: Labels `epic:ux`, `epic:gn`, `epic:ga`, `shell:gtk` → Milestones 19–21 → 32 Issues → zweiter + Lauf setzt „Blockiert durch/Blockiert" mit echten Nummern → Kommentare auf #88, #106, #100, #102, + #66, #63, #73, #82, #83, #85 (GTK-Lesart, Namensraum, Watchlist-Muster). Zuordnungstabelle in + `issues-v1.1-v1.3.md`. +4. Commit `docs(planning): …` auf dem Arbeits-Branch; Push und PR nur nach Bastis OK. diff --git a/docs/mla-next/ISSUES.md b/docs/mla-next/ISSUES.md index 523761d0..6d914248 100644 --- a/docs/mla-next/ISSUES.md +++ b/docs/mla-next/ISSUES.md @@ -1,6 +1,8 @@ # MLA-Next — Issues > Spiegel der GitHub-Issues mit Label `track:mla-next` (Stand 2026-09-30). Zuordnung Schlüssel → Issue-Nummer steht in der Tabelle. +> +> **Hinweis (2026-10-18):** Die Produktplanung V1.1–V1.3 (GTK-Shell nach 0.4.x, Label `shell:gtk`, Milestones 19–21) läuft bewusst **ohne** `track:mla-next` und ist hier nicht gespiegelt — siehe `issues-v1.1-v1.3.md` und `ausbauplan-v1.1-v1.3.md` im Repo-Root. | Schlüssel | Issue | Milestone | |---|---|---| diff --git a/docs/mla-next/MILESTONES.md b/docs/mla-next/MILESTONES.md index d6837e4c..557b97e4 100644 --- a/docs/mla-next/MILESTONES.md +++ b/docs/mla-next/MILESTONES.md @@ -44,3 +44,7 @@ Computer-Use-Evaluation, Wayland-Freigaben, Stop-Schalter, Sicherheits-/Performa Erfolgskriterium: Regressionsmatrix ausgeführt und belegt; Packaging-/Upgrade-Probe belegt; Entscheidung und Rückfallplan dokumentiert. Merge, Release und Policy-Updates nur nach gesonderter Freigabe von Basti. + +## V1.1 – V1.3 (GTK, nach 0.4.x) — Produktplanung ohne `track:mla-next` + +> Nicht Teil des Experiments: Die Milestones **V1.1 – Cockpit-QoL & UX**, **V1.2 – Zorin/GNOME-Integration & Werkzeuge** und **V1.3 – Nice-to-haves & Parkliste** (GitHub-Nr. 19–21, Issues mit Label `shell:gtk`) planen den Ausbau der GTK-Shell **nach** dem 0.4.x-Entscheid (#106) und setzen voraus, dass er für GTK fällt. Beschreibungen und Erfolgskriterien: `meilensteine-v1.1-v1.3.md` (Repo-Root); Bodies: `issues-v1.1-v1.3.md`; Master-Plan mit Rückfallregel: `ausbauplan-v1.1-v1.3.md`. diff --git a/docs/wiki/Roadmap.md b/docs/wiki/Roadmap.md index a03f0e85..a706d53f 100644 --- a/docs/wiki/Roadmap.md +++ b/docs/wiki/Roadmap.md @@ -9,6 +9,10 @@ sind im Repo deaktiviert). `meilensteine-v0.8.1-v0.8.5.md`, `issues-v0.8.1-v0.8.5.md` und `release-plaene-v0.8.1-v0.8.5.md` (Repo-Root). +## Ausbau ab V1.1 (GTK-Shell) + +Nach V1.0 „Cockpit komplett" folgen drei Wellen auf der GTK-Shell: `ausbauplan-v1.1-v1.3.md`, `meilensteine-v1.1-v1.3.md`, `issues-v1.1-v1.3.md` (Repo-Root; GitHub-Milestones 19–21, Label `shell:gtk`). Prämisse ist der GTK-Entscheid aus MLA-Next 0.4.x (#106); die Rückfallregel steht im Ausbauplan. + ## Kandidaten (Stand nach v0.8.0) | Thema | Quelle | Aufwand | diff --git a/issues-v1.1-v1.3.md b/issues-v1.1-v1.3.md new file mode 100644 index 00000000..c96fc5b5 --- /dev/null +++ b/issues-v1.1-v1.3.md @@ -0,0 +1,1202 @@ +# Issues V1.1 – V1.3 — Master Linux Assistant (MLA) + +> 32 Issues, je mit Titel, Meilenstein, Labels und fertigem Body (1:1 kopierbar). +> Labels: vorhanden `epic:{fu,lb,di,sp,st,te,wz,ds}`, `tier:{core,qol,n2h}`, `size:{S,M,L}`, `gate:manual`, `roadmap`; neu: `epic:ux`, `epic:gn`, `epic:ga`, `shell:gtk`. +> Issue-Bodies unterliegen dem Leak-Grep (keine Hosts, IPs, Ports, `/home`-Pfade, keine Unit-, Container- oder Koordinatenwerte des Referenzsystems). +> Abhängigkeiten nennen neue Issues per Kurz-ID (z. B. `FU4`); das Anlege-Skript ersetzt sie durch GitHub-Nummern und setzt die Rückrichtung „Blockiert". Die GitHub-Nummern stehen in der Zuordnungstabelle am Ende. +> Meilensteine: V1.1 = „V1.1 – Cockpit-QoL & UX" · V1.2 = „V1.2 – Zorin/GNOME-Integration & Werkzeuge" · V1.3 = „V1.3 – Nice-to-haves & Parkliste". + +--- + +## Issue 1 — [V1.1] FU4 Generischer Probe-Vertrag über IPC + +**Meilenstein:** V1.1 · **Labels:** `epic:fu`, `tier:core`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +„Ist alles in Ordnung?" braucht einen Weg, auf dem jede Probe aus dem Dart-Kern ohne eigene UI-Verdrahtung in der GTK-Shell ankommt. Befund: `ModuleRegistry`, `ProbeRegistry` und `EventBus` (`CoreTopics.probeResult`) laufen heute nur in den la_core-Tests; `la_probe` kennt nur `--version`/`SelfProbe`; der IPC-Entwurf nennt nur `backup.*`-Methoden. + +## Scope +- GTK-Delta zu #100 (A4 IPC) und #102 (Lagebild): Transport und Peer-Prüfung bleiben #100, die Ampeln bleiben #102 — FU4 macht den Vertrag **probe-generisch**, damit neue Probes keine neuen Wire-Methoden brauchen. +- (a) `la_probe --json ` als CLI-Backend (kein Socket): gibt `ProbeResult{severity, key, params, at}` + `ProbeState` als eine JSON-Zeile aus; `--json all` listet alle registrierten Probes. Das ist die Zwischenstufe für alle ⚡-Probes (ST4, GA1, SP3, DI8 …). +- (b) IPC-Methoden `probe.list`, `probe.get`, `probe.watch` und Notification `probe.changed` mit demselben Payload, registry-getrieben aus `ProbeRegistry`. `backup.*` aus `docs/mla-next/IPC_CONTRACT.md` wird Capability/Spezialfall von `probe.*` (Vertrag dort anpassen; Kommentar auf #100/#102: Namensraum von Anfang an so nutzen). +- (c) `prototype/gtk/probe_store.py`: ein `ProbeStore` mit Backends `fixture` → `cli` (la_probe) → `ipc` (stdlib `socket` AF_UNIX + `GLib.io_add_watch`, 64-KiB-Framelimit **vor** dem Parsen, Seq-Lücke/Disconnect → `stale` + Snapshot-Neuabruf). Module lesen nur aus dem Store. +- Prozess-Modell: IPC-Host = Kindprozess der GTK-App (`Gio.Subprocess`), endet mit ihr. Kein Dauerprozess, kein Autostart. +- Grenzen: GTK-UI und Dart-Host nie als Root; keine Shell-Strings; keine Root-/Exec-Methoden im Wire; polkit-Grenze unberührt; keine Secrets im Payload. + +## Andockpunkte +- `packages/la_core/bin/la_probe.dart` (heute nur `--version`), `packages/la_core/lib/src/probe.dart` (`ProbeResult`, `ProbeSeverity`), `probe_status.dart` (`ProbeState`), `probe_registry.dart`, `event_bus.dart` +- `docs/mla-next/IPC_CONTRACT.md` (Methodenliste), `prototype/gtk/mla_app.py` (`MODULES`, `select_module`) +- Fixtures: `packages/la_core/test/` (Contract-Fixtures je Methode), `prototype/gtk/tests/test_probe_store.py` + +## Abnahme +- [ ] Eine neue Probe im Kern erscheint ohne GTK-Codeänderung im Store und damit in der Ampel-Liste +- [ ] Contract-Fixtures positiv/negativ für `probe.list/get/watch` (falscher Frame, über 64 KiB, unbekannte ID) +- [ ] `la_probe --json ` liefert dasselbe Schema wie die IPC-Antwort (Schema-Test vergleicht beide) +- [ ] Nach Disconnect zeigt der Store `stale` (gestrichelt), nach Reconnect einen vollständigen Snapshot + +## Abhängigkeiten +Blockiert durch: #100, #102 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: #100/#102 bauen eigene Methoden, bevor FU4 greift → FU4 schrumpft auf Store + Contract-Fixtures; bis dahin laufen ⚡-Issues über das `cli`-Backend._ + +_Tier: Core · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic FU (Teilplan FU4)._ +``` + +--- + +## Issue 2 — [V1.1] DS7 l10n-Grundgerüst GTK + +**Meilenstein:** V1.1 · **Labels:** `epic:ds`, `tier:qol`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Job +Jede Satz-Abnahme („erklärt sich in einem Satz", „Was tun?") braucht übersetzbare Strings; die GTK-Shell hat heute weder gettext noch `.po`-Dateien. + +## Scope +- gettext-Domain `linux-assistant`, Quelldateien `prototype/gtk/po/{de,en}.po`, Build-Schritt `msgfmt` (Make-Ziel oder `tool/`-Skript), Laufzeit-Lookup über `gettext.translation`; Hilfsfunktion `_()` in `prototype/gtk/i18n.py`. +- Key-Konvention = ARB-Key der Flutter-App (`lib/l10n/app_en.arb`), damit Probe-`messageKey`s in beiden Shells dieselben Keys treffen. +- Sprache folgt `LANG`/`LANGUAGE`; ein Override kommt mit UX4. +- Ratchet-Test: jeder in `prototype/gtk/*.py` benutzte Key existiert in beiden `.po` (Regex über `_('…')`). +- Grenzen: keine neuen pip-Pakete (gettext ist Standardbibliothek). + +## Andockpunkte +- `prototype/gtk/mla_app.py` (`MODULES`-Titel, Detailtexte), `lib/l10n/app_en.arb` (Key-Vorbild), `.github/workflows/build.yml` (msgfmt-Schritt neben dem Token-Gate) +- Test: `prototype/gtk/tests/test_i18n.py` + +## Abnahme +- [ ] Alle sichtbaren Strings in `mla_app.py` laufen über `_()` +- [ ] Ratchet-Test grün; ein fehlender Key in `de.po` macht den Test rot (Negativ-Fixture) +- [ ] `LANG=de_DE.UTF-8` zeigt deutsche Modultitel, `LANG=C` englische (Test über das Übersetzungsobjekt) + +## Abhängigkeiten +Blockiert durch: — (⚡ sofort startbar) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `msgfmt` fehlt auf dem Runner → `.mo` im Test aus `.po` mit dem Python-Äquivalent erzeugen._ + +_Tier: QoL · Größe: S · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic DS (Teilplan DS7)._ +``` + +--- + +## Issue 3 — [V1.1] UX1 Navigation, Fensterzustand & Tastatur-Grundgerüst + +**Meilenstein:** V1.1 · **Labels:** `epic:ux`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +„Schnell erledigen": Die Shell soll sich wie ein Werkzeug anfühlen, das sich merkt, wo man war, und per Tastatur bedienbar ist. Flutter-Pendants: DS4 (#86, Sidebar/Navigation) und DS3 (#79, Kürzel) — gelten für GTK sinngemäß, der Flutter-UI-Teil entfällt. + +## Scope +- `Adw.OverlaySplitView` statt `Adw.NavigationSplitView`: Sidebar ein-/ausklappbar (Strg+B); unter 1000 px Breite (`Adw.Breakpoint`) eine Rail aus symbolischen Icons — je Modul in `MODULES` ein Icon-Name (z. B. `view-grid-symbolic`, `utilities-system-monitor-symbolic`, `drive-harddisk-symbolic`, `security-high-symbolic`). +- Fensterzustand (Größe, Position soweit der Compositor sie liefert, Paned-Position, letztes Modul, Sidebar-Zustand) atomar (tmp + rename) in `$XDG_CONFIG_HOME/linux-assistant/gtk-state.json` mit `schema`-Feld. +- Kürzel aus einer zentralen Tabelle (`prototype/gtk/shortcuts.py`): Strg+1…9 = Modul, Esc = Dashboard, F5 = Modul neu laden, Strg+B = Sidebar, Strg+? = `Gtk.ShortcutsWindow` (aus derselben Tabelle generiert). +- Header zeigt Modul-Icon + Titel. +- Grenzen: nur UI-Zustand, keine Systemaktionen. + +## Andockpunkte +- `prototype/gtk/mla_app.py`: `Window.__init__` (SplitView, `Gtk.Paned`, `PANE_POSITION`), `MODULES`, `select_module`; Layout-Tokens `docs/mla-next/TOKENS.md` §2 (`layoutPanePos`, `layoutWindow`) +- Hinweis: PR #119 (#96) ändert `mla_app.py` — Andockpunkte nach dessen Merge neu beziehen +- Tests: `prototype/gtk/tests/test_window_state.py` (headless, State-Datei), Widget-Test unter `xvfb-run` + +## Abnahme +- [ ] Neustart stellt Größe, Paned-Position, Sidebar-Zustand und letztes Modul wieder her (State-Datei-Test, headless) +- [ ] Strg+3 wechselt ins dritte Modul; Esc führt zum Dashboard (Widget-Test unter xvfb) +- [ ] Unter 1000 px erscheint die Rail, darüber die volle Sidebar +- [ ] Kürzel-Overlay listet exakt die Einträge der Kürzel-Tabelle (Test vergleicht beide) + +## Abhängigkeiten +Blockiert durch: — (⚡ sofort startbar; nach Merge von PR #119 rebasen) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Fensterposition ist unter Wayland nicht abfragbar → nur Größe/Paned/Modul merken, Position auslassen und dokumentieren._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests` (Widget-Tests unter `xvfb-run`)._ +_Quelle: Ausbauplan V1.1–V1.3, Epic UX (Teilplan UX1)._ +``` + +--- + +## Issue 4 — [V1.1] UX2 Kontext-Inspektor (rechte Leiste) + +**Meilenstein:** V1.1 · **Labels:** `epic:ux`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +Jede Ampel und jede Kachel muss sich erklären: Quelle, Messzeit, Einheit, ein Satz, mögliche Aktionen. Heute ist die rechte Leiste ein `Gtk.Label` mit Platzhaltertext. + +## Scope +- Panel statt Label: Status-Chip (Tone-Klassen aus TOKENS §4), Ein-Satz-Erklärung (messageKey → DS7), Meta-Zeilen Quelle · Messzeit · Einheit, Aktionsliste (nur Hand-offs und bestätigte Aktionen), Verlauf (letzte Übergänge, aus LB4), ein-/ausklappbar (Strg+I), per Tastatur fokussierbar. +- `DetailsProvider`-Protokoll (Python `typing.Protocol`): `details(module_id) -> Details(status, sentence_key, source, measured_at, unit, actions, history)`; je Modul ein Provider, dazu ein Fixture-Provider für alle vier Module. +- Panel-Zustand (offen/zu, Breite) in `gtk-state.json` (UX1). +- Grenzen: keine Systemaktionen aus dem Panel ohne Bestätigung; keine Rohausgaben (Journal, Tokens) im Panel. + +## Andockpunkte +- `prototype/gtk/mla_app.py`: `self.details`, `self.status_chip`, `detail_panel`, `select_module`; `prototype/gtk/tokens.css` (`.mla-details`, `.mla-chip`, `.mla-tone-*`) +- Store aus FU4 (`probe_store.py`) als Datenquelle; bis dahin Fixture-Backend +- Tests: `prototype/gtk/tests/test_details.py` + +## Abnahme +- [ ] Jedes Modul liefert über seinen Provider Details (Fixture-Test je Modul) +- [ ] Eine `stale`-Probe zeigt den gestrichelten Chip und „seit " +- [ ] Panel ist per Strg+I und per Tab erreichbar; Zustand wird gemerkt (xvfb-Test + State-Datei) +- [ ] Verlauf erscheint, sobald LB4 Daten liefert; ohne Daten bleibt die Zeile weg (kein Fehler) + +## Abhängigkeiten +Blockiert durch: UX1 (Zustand), LB4 (Verlauf) · ⚡ Teil A (Panel + Provider) sofort startbar · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Provider-Protokoll wird zu breit → nur die fünf Pflichtfelder festschreiben, Rest optional._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic UX (Teilplan UX2)._ +``` + +--- + +## Issue 5 — [V1.1] UX3 Leer-, Lade-, Stale- und Fehlerzustände + +**Meilenstein:** V1.1 · **Labels:** `epic:ux`, `tier:qol`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Job +Kein leerer Screen, keine lügenden Flächen: Fehlt Docker oder `nvidia-smi`, sagt die Fläche, was fehlt — nie ein Fehler; veraltete Daten sehen nie wie aktuelle aus (TOKENS §4: stale ≠ ok). + +## Scope +- `prototype/gtk/states.py`: Fabriken für `Adw.StatusPage` „nicht verfügbar" (nennt das fehlende Werkzeug und was damit möglich wäre), „lädt" (erst nach 300 ms sichtbar), „stale" (gestrichelte Border + Messzeit), `Adw.Banner` für nicht blockierende Fehler, `Adw.Toast` für Aktionsergebnisse. +- Regel: jedes `Gtk.Stack`-Kind rendert genau einen der Zustände `ok|loading|stale|unavailable|error`. +- HardcodeTests in `prototype/gtk/tests/test_tokens.py` von `mla_app.py` auf alle `prototype/gtk/*.py` erweitern (keine Hex-Farben außerhalb der Token-Dateien). +- Grenzen: nur Darstellung. + +## Andockpunkte +- `prototype/gtk/mla_app.py` (Modul-Boxen in `Window.__init__`), `prototype/gtk/tokens.css` (`.mla-tone-stale`), `prototype/gtk/tests/test_tokens.py` (`HardcodeTests`, Konstante `APP_PY`) +- Tests: `prototype/gtk/tests/test_states.py` + +## Abnahme +- [ ] Alle vier Module lassen sich per Fixture in jeden der fünf Zustände versetzen (Test) +- [ ] Hardcode-Gate deckt alle `.py` unter `prototype/gtk/` ab (Negativ-Fixture mit Hex-Wert macht es rot) +- [ ] Kein Stack-Kind ohne Zustand (Test iteriert über alle Kinder) +- [ ] Galerie-Lauf (DS5) zeigt alle Zustände hell/dunkel — nach DS5 + +## Abhängigkeiten +Blockiert durch: DS5 (nur der Galerie-Lauf) · ⚡ Teil A (`states.py` + Gate) sofort startbar · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `Adw.StatusPage` wirkt in kleinen Kacheln zu groß → kompakte Variante (`compact`-Stilklasse) im selben Modul._ + +_Tier: QoL · Größe: S · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic UX (Teilplan UX3)._ +``` + +--- + +## Issue 6 — [V1.1] UX4 Einstellungen als Seite + +**Meilenstein:** V1.1 · **Labels:** `epic:ux`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +Einstellungen gehören in die Navigation, nicht in einen Dialog (UI-Befund N3; Flutter-Pendant DS4/#86 — gilt für GTK sinngemäß). Watchlists, Ruhezeiten und eigene Aktionen brauchen einen Ort. + +## Scope +- `Adw.PreferencesPage`s im `Gtk.Stack` (Modul „Einstellungen" in `MODULES`), kein Dialog. Seiten: **Allgemein** (Sprache — mit DS7, Start-Modul) · **Wächter** (Takt, Ruhezeiten, Erinnerung; der Schalter `systemctl --user enable --now` wirkt erst mit #66) · **Watchlists** (Unit-Muster, Mounts, Compose-Projekte) · **Exposition** (Baseline „bekannt", Schema aus SI1/#80) · **Eigene Aktionen** (Schema `custom_actions` aus TE2/#73: name, argv-Liste, cwd, mode, confirm, keywords; argv-Editor als Liste, kein Shell-Feld). +- Persistenz in `config.json` (`$XDG_CONFIG_HOME/linux-assistant/`): unbekannte Keys bleiben beim Round-trip erhalten (die Flutter-`ConfigHandler`-Datei ist eine flache Key/Value-Map); Schreiben atomar. +- Import/Export über `Gtk.FileDialog` (JSON). +- Grenzen: nur Konfiguration; keine Ausführung aus den Einstellungen heraus; keine Secrets in `config.json` (Tokens kommen in den Secret Service, LB6/DI6). + +## Andockpunkte +- `prototype/gtk/mla_app.py` (`MODULES`, Stack), neu `prototype/gtk/settings_page.py`, `prototype/gtk/config_store.py` +- Schema-Vorbilder: `lib/services/config_handler.dart` (Dateiformat), Issue #73 (`custom_actions`), Issue #80 (`exposure_baseline`) +- Tests: `prototype/gtk/tests/test_config_store.py` mit Fixture, die Flutter-Keys enthält; Dart-Seite: `test/config_handler_test.dart` liest eine GTK-geschriebene Datei + +## Abnahme +- [ ] Jede Einstellung round-trippt (schreiben → neu laden → gleicher Wert) +- [ ] Unbekannte Keys der Fixture bleiben unverändert erhalten +- [ ] Ungültiges argv (leer, kein String-Array) wird abgewiesen; ein Shell-Feld existiert nicht +- [ ] Flutter-`ConfigHandler` liest die GTK-geschriebene Datei ohne Fehler (Dart-Test) + +## Abhängigkeiten +Blockiert durch: DS7 (nur „Sprache") · ⚡ Teil A sofort startbar · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Key-Kollision mit künftigen Flutter-Keys → Namensraum `gtk.*` für rein GTK-seitige Einstellungen._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests` · Flutter-Gates (`flutter test`) wegen `config.json`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic UX (Teilplan UX4)._ +``` + +--- + +## Issue 7 — [V1.1] DS5 Komponenten-Katalog, Galerie & GTK-CI-Gate + +**Meilenstein:** V1.1 · **Labels:** `epic:ds`, `tier:qol`, `size:M`, `shell:gtk`, `gate:manual` + +### Body + +```markdown +## Job +Neue Module sollen aus denselben Bausteinen entstehen wie der Monitor, nicht aus Kopien; und GTK-Widget-Tests müssen in CI laufen, sonst bleibt jede Abnahme manuell. + +## Scope +- `prototype/gtk/widgets.py`, **extrahiert** aus `monitor_ui.py` (#96 / PR #119): `StatCard` (Wert, Einheit, Sparkline), `AmpelChip` (Tone-Klassen), `MetaRow`, `Sparkline` (`Gtk.Snapshot`-Zeichnung, kein pycairo), `Badge`, `SectionHeader`, `ActionRow`. Gegenstück zum Flutter-Katalog `docs/handoff/components/component-catalog.md`. +- Galerie-Screen (`MLA_SHOW_GALLERY=1`): jede Komponente in allen Tönen (ok/warn/crit/running/unknown/stale), hell/dunkel (`MLA_FORCE_COLOR_SCHEME`), 100/125/150 %. +- Doku `docs/mla-next/COMPONENTS.md` (Komponente → Zweck → Tokens → Beispiel). +- **CI:** Schritt `xvfb-run -a python3 -m unittest discover -s prototype/gtk/tests` mit apt `xvfb gir1.2-gtk-4.0 gir1.2-adw-1` in `.github/workflows/build.yml`. +- Grep-Gate in `test_tokens.py`: `.mla-chip`/`.mla-tone-*`-Klassen und `Gtk.Snapshot`-Zeichnen nur in `widgets.py`. +- Grenzen: nur Darstellung. + +## Andockpunkte +- `prototype/gtk/monitor_ui.py` (Quelle der Extraktion), `prototype/gtk/tokens.css`, `docs/mla-next/TOKENS.md` §4/§8, `.github/workflows/build.yml` (Token-Gate-Schritt) +- Tests: `prototype/gtk/tests/test_widgets.py` (xvfb), Gate in `test_tokens.py` + +## Abnahme +- [ ] Monitor und Dashboard nutzen nur Katalog-Komponenten (Grep-Gate grün, Negativ-Fixture rot) +- [ ] Widget-Tests laufen in CI unter xvfb (Workflow-Lauf grün) +- [ ] Galerie zeigt alle Komponenten in allen Tönen hell/dunkel; Kontrast-Tests bleiben grün +- [ ] Galerie bei 125 % und 150 % ohne Abschneiden gesichtet (manuell, einmalig) + +## Abhängigkeiten +Blockiert durch: #96 (PR #119 liefert `monitor_ui.py`) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: xvfb-Lauf flackert → Widget-Tests nur Konstruktion/Property-Checks, keine Pixel-Vergleiche._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `xvfb-run -a python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic DS (Teilplan DS5)._ +``` + +--- + +## Issue 8 — [V1.1] LB4 Ampel-Feinschliff: Bekannt, Snooze, Ruhezeiten, Verlauf + +**Meilenstein:** V1.1 · **Labels:** `epic:lb`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +Eine Ampel, die rot bleibt, weil man den Zustand kennt, erzieht zum Wegschauen. Bekannt-Markierung, Erinnerung später, Ruhezeiten und ein Verlauf halten das Lagebild glaubwürdig. Baut auf #102 (Lagebild GTK) und #66 (Wächter) auf — beide gelten für GTK sinngemäß (Kern = la_core/la_probe). + +## Scope +- `state.json` (`$XDG_STATE_HOME/linux-assistant/`, Schema in la_core, Schreiber GTK, Leser la_probe): je Probe `known` (bis zum nächsten Zustandswechsel; gemeinsames Schema mit SI1/#80 `exposure_baseline`), `snoozed_until` (1 h / bis morgen), global `quiet_hours` (z. B. 22–08 Uhr). +- Der Wächter (`la_probe`) respektiert `state.json`: bekannte/gesnoozte Probes zählen nicht zur Gesamtfarbe; in Ruhezeiten keine Benachrichtigung, die Anzeige bleibt. +- `status-history.jsonl` (la_probe schreibt bei jedem Übergang, Ring 30 Tage) → „seit wann" und die letzten Übergänge je Probe im Kontext-Inspektor (UX2). +- „Was tun?"-Satz je Probe (messageKey, DS7) mit Sprung ins Detail. +- Kommentar auf #66: Historie-Datei und `state.json`-Lesen dort einplanen. +- Grenzen: nie Root; Dateien atomar; keine Secrets. + +## Andockpunkte +- `packages/la_core/lib/src/probe_status.dart`, `probe.dart`; neu `lib/src/state_file.dart` (Schema `known/snoozed_until/quiet_hours`) und `history_file.dart` +- `prototype/gtk/probe_store.py` (FU4), Kontext-Inspektor (UX2), `prototype/gtk/settings_page.py` (UX4: Ruhezeiten) +- Fixtures: `packages/la_core/test/fixtures/state_*.json`, Fixture-Uhr für den Snooze-Ablauf + +## Abnahme +- [ ] Bekannt-markierte Ampel zählt nicht zur Gesamtfarbe und meldet nicht (la_core-Test) +- [ ] Snooze läuft mit der Fixture-Uhr ab und die Ampel meldet wieder +- [ ] In Ruhezeiten unterdrückt der Wächter die Benachrichtigung; der Übergang steht trotzdem in der History +- [ ] Inspektor zeigt „seit " und die letzten drei Übergänge aus einer Fixture-History + +## Abhängigkeiten +Blockiert durch: #102, #66, FU4 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Zwei Schreiber auf `state.json` (GTK + Wächter) → der Wächter liest nur; einzige Schreibstelle ist die GTK-App._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic LB (Teilplan LB4)._ +``` + +--- + +## Issue 9 — [V1.1] LB5 Tages-Check „Heute" + +**Meilenstein:** V1.1 · **Labels:** `epic:lb`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +„Ist alles in Ordnung?" einmal am Tag als Liste statt als Suche über alle Kacheln: Neustart, kaputte Units, letztes Backup, Platte, Updates, Grafik-Ereignisse, Agenten-Blocker, Restore-Probe. + +## Scope +- Dashboard-Karte „Heute", beim ersten Öffnen des Tages ausgeklappt (Marker in `gtk-state.json`). Zeilen entstehen nur aus vorhandenen Probes; fehlt eine Quelle, fehlt die Zeile (kein Fehler, kein Platzhalter): Neustart nötig (#58-Q2) · fehlgeschlagene Units (#58-Q3/#102) · letztes Backup (#63/#101) · Platten-Prognose (#83) · ausstehende Updates, Kernel/NVIDIA markiert (#82) · Grafik-Ereignisse letzter Boot (ST4) · Agenten-Blocker (#69) · **„Restore zuletzt geprobt vor N Tagen"** aus config `last_restore_test` (ein erfolgreicher Unit-Lauf ist kein Restore-Nachweis; Schwelle `restore_test_warn_days`, Default 90). +- Zeilen abhakbar („erledigt" bis zum nächsten Zustandswechsel, `state.json` aus LB4); leer → „Alles in Ordnung". +- Grenzen: nur Lesen; Aktionen sind Sprünge in die Module. + +## Andockpunkte +- `prototype/gtk/probe_store.py` (FU4), Dashboard-Modul in `mla_app.py`, `widgets.py` (DS5: `ActionRow`), `state_file.dart` (LB4) +- Fixtures: Probe-Sätze mit 0, 2 und 8 roten Probes; einer ohne Backup-/Update-Quelle + +## Abnahme +- [ ] Fixture mit zwei roten Probes → genau zwei Zeilen; Fixture ohne rote → „Alles in Ordnung" +- [ ] Abgehakte Zeile bleibt weg, bis die Probe den Zustand wechselt +- [ ] Fehlende Quellen (kein Backup-, kein Update-Probe) erzeugen keine Zeile und keinen Fehler +- [ ] Restore-Zeile wird gelb, wenn `last_restore_test` älter als die Schwelle ist (Fixture-Uhr) + +## Abhängigkeiten +Blockiert durch: #102, #66, FU4 (Pflicht) · nutzt, wenn vorhanden: #58, #63, #101, #82, #83, #69, ST4 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Zu viele Zeilen → nur rot/gelb anzeigen, grün zusammenfassen._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic LB (Teilplan LB5)._ +``` + +--- + +## Issue 10 — [V1.1] ST4 Grafik-Stabilitäts-Wache (NVIDIA/Wayland) + +**Meilenstein:** V1.1 · **Labels:** `epic:st`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +„Was ist passiert?" nach einem eingefrorenen Desktop: Auf dem Referenzsystem traten wiederholt Treiber-Livelocks unter Wayland auf, einmal hing sogar der Neustart; das Runbook dafür liegt außerhalb der App. GTK-Delta zu ST1/#76 (Boots, Journal-Grep, SysRq, Runbook-Link sind dort spezifiziert — gilt für GTK sinngemäß, Kern = la_core/la_probe, die Fixture wird geteilt). + +## Scope +- Probe `gpu_stability` in la_core, Quellen als Nutzer (Gruppe `adm`): `journalctl --list-boots -o json`, `journalctl -k -b -o json` mit Markern `nvidia-modeset`, `nvkms`, `Xid`, `blocked for more than`, `NVRM`; unsaubere Boots (fehlender Shutdown-Marker); Treiberversion (`nvidia-smi --query-gpu=driver_version --format=csv,noheader`) gegen config `known_bad_drivers` (Liste exakter Versionsstrings); Sitzungstyp (`XDG_SESSION_TYPE`). +- **Neu gegenüber #76 — Rettungs-Bereitschaft:** `/proc/sys/kernel/sysrq` (Wert), persistentes Journal (`/var/log/journal` vorhanden), `/dev/watchdog` (vorhanden/nicht vorhanden — beides gültige Zustände), Zustand des OC-Dienstes (config `oc_service`, `systemctl is-enabled/is-active`). +- Karte „Grafik" + Inspektor-Detail: Ereignisse je Boot, „Runbook öffnen" (config `runbook_url`, Datei oder URL, WZ3-Hand-off), SysRq-Leiter als kopierbarer Text. Ohne NVIDIA → „nicht zutreffend" (UX3). +- Fixture `test/fixtures/zorin_journal_freeze.txt` (JSONL) mit Streichliste: `_HOSTNAME`, `_MACHINE_ID`, `_BOOT_ID`, `_CMDLINE`, Seriennummern, MAC-Adressen; der Leak-Check (`additional/python/tests/test_fixture_leak_check.py`) muss die Datei erfassen. +- Grenzen: nur Lesen; kein Root; keine Rohzeilen mit Hostnamen im UI. + +## Andockpunkte +- `packages/la_core/lib/src/probe.dart`, neu `lib/src/probes/gpu_stability.dart`, Parser `lib/src/parsers/journal.dart`; CLI über `la_probe --json gpu_stability` (FU4-Zwischenstufe) +- `prototype/gtk/` Karte über `widgets.py`/`probe_store.py` +- Verweise: ST5 (Setup-Check), DI8 (Tailscale-SSH als Rettungsweg) + +## Abnahme +- [ ] Fixture-Boots mit Freeze-Signatur → Ampel rot, Ereignisliste je Boot (Dart-Test) +- [ ] Bereitschaft zeigt sysrq, Journal, Watchdog, OC-Dienst als eigene Zeilen, „nicht vorhanden" inklusive +- [ ] Treiberversion aus `known_bad_drivers` → rot mit Satz +- [ ] Ohne `nvidia-smi` / ohne Journal-Zugriff: „nicht zutreffend" / „keine Berechtigung", kein Fehler + +## Abhängigkeiten +Blockiert durch: — (⚡ sofort startbar; ST1/#76 als Referenz) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Journal-Fixture zu groß → auf die Boots mit Signatur plus einen sauberen Boot kürzen._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic ST (Teilplan ST4)._ +``` + +--- + +## Issue 11 — [V1.1] GA1 Gaming-Vorflug + +**Meilenstein:** V1.1 · **Labels:** `epic:ga`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +„Vor dem Spielen prüfen": Power-Profil, GameMode, Thermik, OC-Dienst, Speicherdruck, freier Platz und Standby-Blocker auf einen Blick, bevor das Spiel startet — statt vier Terminals. + +## Scope +- Probe `gaming_readiness` in la_core: `gamemoded -s`, `powerprofilesctl get`, EPP (`/sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference`), GPU Temp/Takt/VRAM/Power-Limit (`nvidia-smi --query-gpu=temperature.gpu,clocks.gr,memory.used,memory.total,power.limit --format=csv,noheader`), OC-Dienst (config `oc_service`), PSI/zram (bestehende Parser), freier Platz je Steam-Bibliothek (GA2; Fallback Mount-Prüfung), Steam läuft (`pgrep -x steam`), Standby-Inhibitoren (`systemd-inhibit --list --no-pager`). +- Schwellen als config-Defaults: `gaming.temp_warn_c` = 80, `gaming.free_gb_warn` = 10, `gaming.psi_warn` = 10. +- Karte „Gaming" als Checkliste mit Ein-Satz-Hinweisen (DS7-Keys). +- **Eine bestätigte Aktion:** Power-Profil umschalten über `powerprofilesctl set ` (User-D-Bus, kein Root). Alles andere sind Hand-offs (Steam öffnen) oder eigene Aktionen (TE4). +- Grenzen: nie Root; argv-Liste; ohne GameMode/NVIDIA degradiert zu „nicht installiert". + +## Andockpunkte +- `packages/la_core/lib/src/probes/gaming_readiness.dart`, Parser für `nvidia-smi`-CSV und `systemd-inhibit`; `la_probe --json gaming_readiness` +- `prototype/gtk/` Karte über `widgets.py`; Bestätigung über `Adw.AlertDialog` +- Fixtures: `test/fixtures/gaming_*.txt` (balanced/performance, 60/85 °C, Inhibitoren vorhanden/leer) + +## Abnahme +- [ ] Fixture „balanced + 85 °C + 3 GB frei" → drei gelbe Hinweise mit Satz +- [ ] Profil-Umschaltung nur nach Bestätigung; argv-Assert `powerprofilesctl set performance` +- [ ] Inhibitoren-Fixture → Zeile „Standby blockiert durch …" ohne Prozess-Argumente +- [ ] Ohne `gamemoded`/`nvidia-smi` → „nicht installiert", kein Fehler + +## Abhängigkeiten +Blockiert durch: — (⚡ sofort startbar; GA2 optional für den Bibliotheks-Platz) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `powerprofilesctl set` scheitert ohne power-profiles-daemon → Aktion ausblenden, Hinweis._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic GA (Teilplan GA1)._ +``` + +--- + +## Issue 12 — [V1.1] SP3 Speicher-Feinschliff: Mounts, Timeshift, SMART + +**Meilenstein:** V1.1 · **Labels:** `epic:sp`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +„Platz schaffen, bevor es knallt": Auf dem Referenzsystem lief das Datenlaufwerk voll und der Timeshift-Zeitplan regredierte unbemerkt auf monatlich. Mount-Ampeln, Soll-Abgleich und ein SMART-Warnflag ohne Root schließen die Lücke. Referenz SP1/#83 (Prognose) und LB2/#63 (Backup-Units; Auslagerungs-Jobs laufen dort als Watchlist-Muster `*rclone*`, kein eigener Code) — gelten für GTK sinngemäß. + +## Scope +- Probe `storage_health` in la_core: Mounts mit Ampel 85/90/95 % (`DeviceInfo`-Parser), Prognose-Zeile nur wenn `disk-history.jsonl` (#83) existiert. +- Timeshift-Soll-Abgleich: `/etc/timeshift/timeshift.json` (lesbar als Nutzer) `schedule_*`/`count_*` gegen config `timeshift_expected` (z. B. `{"daily": 2, "monthly": 1}`) → gelb bei Abweichung mit Satz „Zeitplan weicht ab: täglich 1 statt 2". Kein Snapshot-Listing über den privilegierten Sicherheits-Report (keine neue Root-Naht). +- SMART-Warnflag ohne Root: UDisks2 `org.freedesktop.DBus.ObjectManager.GetManagedObjects` auf dem System-Bus → `org.freedesktop.UDisks2.NVMe.Controller` `SmartCriticalWarning`/`SmartTemperature` bzw. `Drive.Ata` `SmartFailing` → rot/gelb. +- Karte „Speicher" mit Hand-off „Aufräumen" (SP2/#84). +- Grenzen: nur Lesen; kein Root; keine Remotes/Tokens. + +## Andockpunkte +- `packages/la_core/lib/src/parsers/df.dart` (`DeviceInfo`), neu `lib/src/probes/storage_health.dart`, Parser `timeshift_config.dart`, D-Bus-Abfrage über `busctl --json=short call` (argv) statt eigener D-Bus-Bibliothek +- Fixtures: `test/fixtures/timeshift_*.txt`, `udisks2_managed_objects_*.txt`; `df`-Fixtures vorhanden + +## Abnahme +- [ ] Fixture `count_daily: 1` bei Soll 2 → gelb mit Satz; passende Werte → grün +- [ ] Mount bei 92 % → rot, bei 86 % → gelb (Schwellen-Test) +- [ ] `SmartCriticalWarning` ≠ 0 in der Fixture → rot; ohne UDisks2-Antwort → „unbekannt" +- [ ] Ohne Timeshift → „nicht installiert", kein Fehler + +## Abhängigkeiten +Blockiert durch: #83 (nur Prognose-Zeile) · ⚡ Teil A sofort startbar · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `busctl`-JSON-Format variiert → Parser-Fixtures je Variante, sonst SMART-Zeile „unbekannt"._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic SP (Teilplan SP3)._ +``` + +--- + +## Issue 13 — [V1.2] GN4 App-ID, Desktop-Einbindung & Autostart + +**Meilenstein:** V1.2 · **Labels:** `epic:gn`, `tier:qol`, `size:S`, `shell:gtk`, `gate:manual` + +### Body + +```markdown +## Job +Schritt 0 für jede GNOME-Naht: Suchanbieter, Benachrichtigungs-Aktionen und Hotkey brauchen eine D-Bus-aktivierbare App mit fester ID, Desktop-Actions und einem symbolischen Icon. + +## Scope +- App-ID: vorläufig `dev.toqsick.MlaPrototype` (Tests), final nach #106 — der Entscheid fällt dort, hier gibt es nur eine Konstante. +- `packaging/.desktop`: `DBusActivatable=true`, `StartupWMClass`, Actions Dashboard / Suche / Systemmonitor / „Backup jetzt" → `gapplication action open "''"`. +- `packaging/.service` für `/usr/share/dbus-1/services/` (Muster: vorhandene Search-Provider-Services auf GNOME). +- `Gio.Application`: `Gio.SimpleAction("open", GLib.VariantType("s"))` → Modul öffnen und Fenster `present()`. +- Symbolisches Icon `-symbolic.svg` (einfarbig) neben dem Farb-Icon; AppStream `packaging/.metainfo.xml` (ohne Screenshots). +- Autostart „beim Login minimiert starten": `~/.config/autostart/.desktop` (XDG-Autostart, `X-GNOME-Autostart-Delay`), Schalter in UX4, Standard aus. Das Background-Portal ist nur optional (die Host-App-ID stammt aus dem systemd-Scope und fehlt bei Start aus dem Terminal). Der Wächter bleibt systemd-Timer. +- Grenzen: keine Systemdateien schreiben — `/usr/share/…` kommt über das .deb (#105). + +## Andockpunkte +- `prototype/gtk/mla_app.py` (`App.__init__`, `application_id`, `do_activate`), neu `packaging/` (Desktop, Service, Metainfo, Icon), `build-deb.sh` (#105 übernimmt die Pfade) +- Tests: `prototype/gtk/tests/test_desktop_files.py` (`desktop-file-validate`, `appstreamcli validate` wenn vorhanden, Action-Namen gegen `MODULES`) + +## Abnahme +- [ ] `desktop-file-validate` und `appstreamcli validate` grün (CI, sofern die Tools auf dem Runner sind; sonst Test übersprungen und benannt) +- [ ] `gapplication action open "'backup'"` öffnet das Backup-Modul bei laufender App; bei gestoppter App nach Installation des .deb (#105) +- [ ] Dock-Kontextmenü zeigt die vier Actions (manuell) +- [ ] Autostart-Schalter erzeugt genau eine Datei und entfernt sie beim Ausschalten + +## Abhängigkeiten +Blockiert durch: #105, #106 (nur Teil B: Systempfade, finale ID) · ⚡ Teil A sofort startbar · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: ID-Wechsel nach #106 → alle Dateien tragen die ID nur über eine Konstante/Template._ + +_Tier: QoL · Größe: S · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic GN (Teilplan GN4)._ +``` + +--- + +## Issue 14 — [V1.2] GN1 GNOME-Shell-Suchanbieter + +**Meilenstein:** V1.2 · **Labels:** `epic:gn`, `tier:qol`, `size:M`, `shell:gtk`, `gate:manual` + +### Body + +```markdown +## Job +Super-Taste drücken, „backup" tippen, Enter — und im MLA steht das Backup-Modul. Der GNOME-Weg für „Schnell erledigen", ohne eigenen Hotkey. + +## Scope +- `prototype/gtk/search_provider.py`: `org.gnome.Shell.SearchProvider2` über `Gio.Application.do_dbus_register(connection, object_path)` + `connection.register_object(path, Gio.DBusNodeInfo.new_for_xml(XML).interfaces[0], handler)`; Methoden `GetInitialResultSet`, `GetSubsearchResultSet`, `GetResultMetas`, `ActivateResult`, `LaunchSearch`. +- `.ini` (`packaging/.search-provider.ini`: `DesktopId`, `BusName`, `ObjectPath`) nach `/usr/share/gnome-shell/search-providers/` über das .deb (#105) — die Shell liest nur System-Datadirs. +- Dienst-Modus: `Gio.ApplicationFlags.IS_SERVICE`, `--gapplication-service` im `.service`, `hold()`/`release()` mit Inaktivitäts-Timeout (z. B. 60 s); Fenster nur bei `ActivateResult`/`LaunchSearch`. +- Treffer: Module, Probe-Aktionen, eigene Aktionen (TE4). Notiztitel nur bei config `search_provider.notes = true` (Standard aus — Privatsphäre, UI-Befund M6). Die Vorschau zeigt nie private Daten. +- Grenzen: kein Dauerprozess ohne Timeout; keine Ausführung aus der Suche ohne die üblichen Bestätigungen. + +## Andockpunkte +- `prototype/gtk/mla_app.py` (`App`), GN4 (App-ID, `.service`, `app.open`), DS7 (Treffertitel), TE4 (Aktionsliste) +- Tests: `prototype/gtk/tests/test_search_provider.py` — headless auf dem Session-Bus mit Test-Busnamen (`dbus-run-session`) + +## Abnahme +- [ ] `GetInitialResultSet("backup")` liefert die Modul-ID, `GetResultMetas` Titel + Icon (Test unter `dbus-run-session`) +- [ ] Super + „backup" zeigt den Eintrag; Enter öffnet das Modul (manuell, nach Paketinstallation) +- [ ] Der Service beendet sich nach dem Timeout ohne Fenster (Test über den `hold`-Zähler) +- [ ] Notiztitel erscheinen nur bei aktiviertem Schalter (Fixture) + +## Abhängigkeiten +Blockiert durch: GN4, #105 · Notizen: #98 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Die Shell cached die Provider-Liste → Abnahme nach Relogin dokumentieren._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic GN (Teilplan GN1)._ +``` + +--- + +## Issue 15 — [V1.2] GN2 Hotkey-Robustheit & Portal-Pfad + +**Meilenstein:** V1.2 · **Labels:** `epic:gn`, `tier:qol`, `size:S`, `shell:gtk`, `gate:manual` + +### Body + +```markdown +## Job +Der Hotkey muss in Wayland und X11 zuverlässig das Fenster holen, ohne verwaiste Einträge zu hinterlassen. Beleg: GNOME 46 / xdg-desktop-portal-gnome 46.x bieten **kein** GlobalShortcuts-Portal — gsettings bleibt der Weg. GTK-Delta zu #58-Q4 (Hotkey über gsettings in beiden Sessions; gilt für GTK sinngemäß). + +## Scope +- `prototype/gtk/hotkey.py` = GNOME-Port von `additional/python/setup_keybinding.py`: Custom-Keybinding idempotent anlegen (`org.gnome.settings-daemon.plugins.media-keys` `custom-keybindings`), verwaiste `custom*`-Einträge mit MLA-Kommando erkennen und entfernen; Kommando = `gapplication launch ` (D-Bus-Activation, kein Binärpfad); Modifier-Regel wie `Linux.getHotkeyModifier()` (Alt+Q auf Zorin/Ubuntu/KDE/Pop, sonst Super+Q). +- Diagnosezeile in UX4: aktuelles Binding, Session-Typ, verwaiste Einträge, Knopf „Reparieren". +- Portal-Erkennung: Introspektion von `org.freedesktop.portal.Desktop` auf `org.freedesktop.portal.GlobalShortcuts`; vorhanden (GNOME ≥ 48) → Hinweis „Portal verfügbar" + Folge-Issue. Keine universellen Wayland-Hotkeys versprechen (Nie-Liste). +- Grenzen: nur gsettings des Nutzers; kein Root. + +## Andockpunkte +- `additional/python/setup_keybinding.py` (Vorlage), `lib/services/linux.dart` (`getHotkeyModifier`, Regel), GN4 (App-ID), UX4 (Diagnosezeile) +- Tests: `prototype/gtk/tests/test_hotkey.py` mit gsettings-Fake (Dict) und Introspektions-Fixture + +## Abnahme +- [ ] Hotkey holt das Fenster in Wayland und in X11 (manuell, beide Sessions) +- [ ] Verwaister Eintrag im Fake wird erkannt und entfernt; ein zweiter Lauf ändert nichts (Idempotenz-Test) +- [ ] Portal-Hinweis erscheint nur mit einer Introspektions-Fixture, die das Interface enthält + +## Abhängigkeiten +Blockiert durch: UX4 (nur Diagnosezeile), GN4 (App-ID) · ⚡ Teil A (`hotkey.py`) sofort startbar · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Desktop-Layouts belegen Alt+Q → Konflikt erkennen und ein alternatives Binding vorschlagen._ + +_Tier: QoL · Größe: S · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic GN (Teilplan GN2)._ +``` + +--- + +## Issue 16 — [V1.2] GN3 Benachrichtigungen mit Aktionen & Deep-Links + +**Meilenstein:** V1.2 · **Labels:** `epic:gn`, `tier:qol`, `size:M`, `shell:gtk`, `gate:manual` + +### Body + +```markdown +## Job +Der Wächter meldet „Backup rot" — ein Klick auf „Öffnen" muss im Backup-Modul landen, auch wenn die App gerade nicht läuft. GTK-Delta zu LB3/#66 („Öffnen → Ziel-Sektion", dort offen gelassen; `raise:
` wird auf GTK zu `app.open`). + +## Scope +- Entscheid für die Lücke aus #66: Der Wächter startet die Benachrichtigung als transiente Einheit `systemd-run --user --collect -- notify-send --wait -A open=Öffnen …` und ruft bei Auswahl `gapplication action open "''"`; die D-Bus-Activation über die `.service`-Datei (GN4) startet die App bei Bedarf. Der Timer-Lauf blockiert nicht. +- In-App-Ereignisse über `Gio.Notification` mit Aktion `app.open`. +- Ruhezeiten und Entprellung aus LB4 (nur Übergänge nach Rot). +- Kommentar auf #66 mit dem Entscheid. +- Grenzen: keine privaten Daten im Benachrichtigungstext (nur Probe-Titel + Satz). + +## Andockpunkte +- `packages/la_core/bin/la_probe.dart` (Wächter-Lauf, #66), neu `lib/src/notify.dart` (argv-Erzeugung), GN4 (`app.open`), LB4 (`state.json`) +- Tests: Dart-Fixture prüft die erzeugte argv-Kette; `prototype/gtk/tests/test_notifications.py` für `Gio.Notification` + +## Abnahme +- [ ] „Backup rot" → „Öffnen" landet im Backup-Modul, bei laufender und bei gestoppter App (manuell) +- [ ] Ein zweiter Lauf ohne Zustandsänderung erzeugt keine Benachrichtigung (la_core-Test) +- [ ] Die argv-Kette (`systemd-run …`, `gapplication action …`) ist exakt gepinnt (Fixture) +- [ ] Ruhezeit unterdrückt die Benachrichtigung (LB4-Fixture) + +## Abhängigkeiten +Blockiert durch: GN4, #66, FU4 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `notify-send --wait` liefert die Aktion auf GNOME nicht zuverlässig → `gdbus call org.freedesktop.Notifications.Notify` + `ActionInvoked`-Signal im Wächter-Kindprozess._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic GN (Teilplan GN3)._ +``` + +--- + +## Issue 17 — [V1.2] DS6 Tastatur, Accessibility & Darstellung folgt GNOME + +**Meilenstein:** V1.2 · **Labels:** `epic:ds`, `tier:qol`, `size:M`, `shell:gtk`, `gate:manual` + +### Body + +```markdown +## Job +Die Shell muss vollständig per Tastatur bedienbar sein, Ampeln müssen vom Screenreader verstanden werden, und Systemeinstellungen (Skalierung, Animationen, Kontrast) gelten auch in der App. Rest zu #97 (Fokus/Kontrast/Langlauf im Monitor). + +## Scope +- `update_property(Gtk.AccessibleProperty.DESCRIPTION, …)` für Ampel-Chips („Backup: kritisch seit "), Rollen/Labels für Karten und Aktionen; Mnemonics in Dialogen; Tab-Reihenfolge je Modul festgelegt. +- Systemeinstellungen: `gtk-enable-animations` → keine Sparkline-Animation; `text-scaling-factor` und 100/125/150 % ohne Abschneiden; High-Contrast (`Adw.StyleManager` `high-contrast`) ohne eigene Farben (die Tokens genügen). +- Automatisierter Fokus-Walk unter `xvfb-run`: Der Tab-Durchlauf erreicht jede interaktive Fläche. +- Orca-Durchgang einmalig manuell (Dashboard, Backup, Einstellungen). +- Grenzen: nur Darstellung. + +## Andockpunkte +- `prototype/gtk/widgets.py` (DS5: `AmpelChip`), `mla_app.py`, `docs/mla-next/TOKENS.md` §6 (Fokusregeln) +- Tests: `prototype/gtk/tests/test_a11y.py` (xvfb) + +## Abnahme +- [ ] Fokus-Walk erreicht jede interaktive Fläche (Test zählt Widgets vs. Fokusstopps) +- [ ] Jeder Ampel-Chip trägt eine `accessible-description` mit Zustand und Zeit (Test) +- [ ] Reduce-Motion deaktiviert die Sparkline-Animation (Test mit gesetzter Einstellung) +- [ ] 150 % ohne Abschneiden; Orca liest Ampeln korrekt vor (manuell) + +## Abhängigkeiten +Blockiert durch: #97, DS5 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Orca-Befund erfordert Widget-Umbau → nur die Chips umbauen, Rest als Folge-Issue._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `xvfb-run -a python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic DS (Teilplan DS6)._ +``` + +--- + +## Issue 18 — [V1.2] WZ3 Werkstatt-Übergaben & Dashboard-Links + +**Meilenstein:** V1.2 · **Labels:** `epic:wz`, `tier:qol`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Job +Integrieren statt nachbauen: Terminal, Dateien, Obsidian und externe Dashboards sind einen Klick entfernt, ohne dass die App sie einbettet (Nie-Liste). Referenz #99 (Browser-/Terminal-Aktionen im Dokument-Reviewer), WZ1/#85 (der n2h-Grafana-Link läuft hier), #110 Position C (PATH-Entscheid). + +## Scope +- `prototype/gtk/handoffs.py` + `ActionRow` (DS5): „Im Terminal öffnen" (Kandidaten in dieser Reihenfolge: `x-terminal-emulator`, `gnome-terminal`, `kgx`, `ptyxis`; Arbeitsverzeichnis per `--working-directory`; **nie `bash -c`**), „In Dateien zeigen" (`Gtk.FileLauncher.open_containing_folder`), „In Obsidian öffnen" (`Gtk.UriLauncher`, `obsidian://open?path=`), „Dashboard im Browser" (config `dashboard_links`: `{"monitor": "", …}`, `Gtk.UriLauncher`; kein Einbetten). +- Start detached mit eigenem Scope über `Gio.AppInfo.create_from_commandline(argv, …).launch()`; Programmsuche über `GLib.find_program_in_path` mit der festen Kandidatenliste (Entscheid #110-C: keine freie PATH-Auflösung von Nutzereingaben). +- Die Ausführung *eigener Befehle* im Terminal lebt in TE4, nicht hier. +- Grenzen: argv-Listen; keine Shell; kein Root. + +## Andockpunkte +- `prototype/gtk/mla_app.py` (Module Dateien/Notizen/Monitor aus #98/#96), `widgets.py`, UX4 (`dashboard_links`) +- Tests: `prototype/gtk/tests/test_handoffs.py` (argv-Assert, Kandidatenwahl mit gefaktem `find_program_in_path`) + +## Abnahme +- [ ] Jede Übergabe erzeugt eine argv-Liste ohne Shell-String (Assert je Aktion) +- [ ] Fehlt das Ziel (kein Terminal, kein Obsidian), erscheint ein Hinweis-Toast statt eines Fehlers +- [ ] Link-Knopf nur sichtbar, wenn `dashboard_links` den Schlüssel enthält; keine URL im Code (Grep-Gate) +- [ ] Dateimanager, Notizen und Monitor nutzen dieselbe Aktionsliste (Test über die Registrierung) + +## Abhängigkeiten +Blockiert durch: — (⚡ sofort startbar; #99/#98 liefern die Module, in denen die Aktionen erscheinen) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `Gtk.FileLauncher` fehlt in älterem GTK → `gio open ` als argv._ + +_Tier: QoL · Größe: S · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic WZ (Teilplan WZ3)._ +``` + +--- + +## Issue 19 — [V1.2] TE4 Palette-Aktionen & Schnellaktionen-Kacheln (GTK) + +**Meilenstein:** V1.2 · **Labels:** `epic:te`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +Strg+K, drei Buchstaben, Enter: Module, Probe-Aktionen, Einstellungen und eigene Skripte aus einer Palette; die wichtigsten eigenen Aktionen als Kacheln auf dem Dashboard (Pendant zu den Flutter-Schnellaktionen). GTK-Delta zu TE2/#73 (Schema `custom_actions`, Bestätigungsmuster) und #99 (Suchpalette); Rechner/Einheiten bleiben in TE3/#74. + +## Scope +- Palette (Strg+K, aus #99) um Provider erweitern: Module, Probe-Aktionen (FU4), Einstellungen (UX4), eigene Aktionen aus `config.json` `custom_actions` (#73: `name`, `argv`, `cwd`, `mode` `output|terminal|detached`, `confirm`, `keywords`, neu `pinned`). +- Dashboard-Abschnitt „Schnellaktionen": alle `pinned`-Aktionen als Kacheln. +- Bestätigung nach WP-S1-Muster (`Adw.AlertDialog` mit vollem argv). Modus `terminal`: Terminal-Kandidaten aus WZ3, Übergabe als `-- `, **nie `bash -c`**; sudo fragt im Terminal selbst. Modus `output`: stdout/stderr im `Adw.Dialog` mit Kopieren. Modus `detached`: `Gio.AppInfo`-Start. +- Grenzen: argv-Listen; nie pkexec; keine Shell; Sonderzeichen-Test wie in #99. + +## Andockpunkte +- Palette aus #99 (Datei wie dort benannt, z. B. `prototype/gtk/palette.py`), `handoffs.py` (WZ3), `config_store.py` (UX4), Dashboard-Modul +- Tests: `prototype/gtk/tests/test_palette.py` (Keyword-Fixture, Sonderzeichen), `test_custom_actions.py` (argv-Assert je Modus) + +## Abnahme +- [ ] Modus `terminal` startet mit Bestätigung; der argv-Assert zeigt ` -- ` ohne Shell +- [ ] `pinned` round-trippt in `config.json`; die Kachel erscheint/verschwindet entsprechend +- [ ] Die Palette findet eine Aktion über `keywords` (Fixture) und zeigt Treffer aus allen Providern +- [ ] Sonderzeichen in `argv` (Leerzeichen, Anführungszeichen, `$`) kommen unverändert an (Test) + +## Abhängigkeiten +Blockiert durch: #99, DS7 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: #99 liefert keine erweiterbare Palette → Provider-Schnittstelle hier nachrüsten (kleiner Umbau, im Issue vermerken)._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests` · Flutter-Gates wegen `config.json`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic TE (Teilplan TE4)._ +``` + +--- + +## Issue 20 — [V1.2] DI4 Stack-Operationen & Container-Hygiene + +**Meilenstein:** V1.2 · **Labels:** `epic:di`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +„Meine Dienste im Griff": Compose-Projekte als Einheit starten/stoppen, alte Images und Build-Cache sehen, bevor die Platte voll ist. Baut auf DI2/#68 (Compose-Gruppierung, Expositions-Flags, Logs — gilt für GTK sinngemäß) auf; die Docker-Kategorie von SP2/#84 verlinkt hierher. + +## Scope +- Je Compose-Projekt: `down`, `stop`, `start`, `restart` über `docker compose -p …` nach Bestätigung; `up` nur mit `--project-directory` und `-f` aus den Container-Labels `com.docker.compose.project.working_dir` / `com.docker.compose.project.config_files`. +- Lose Container (ohne Projekt-Label) als eigene Gruppe. +- Image-Alter und „neueres Image lokal vorhanden?" (`docker image inspect`, `RepoDigests`). +- `docker system df -v` → Karte „Aufräumbar" (dangling Images, ungenutzte Volumes, Build-Cache) mit Vorschau → `docker image prune`, `docker builder prune` nach Bestätigung (Nutzer-Aktionen, kein Root). +- Expositions-Zusammenfassung je Stack (Flags aus DI2); „Compose-Datei öffnen" (WZ3). +- UI-Hinweis: Mitgliedschaft in `docker` ≈ Root. +- Grenzen: argv-Listen; keine Shell; kein `docker rm -f` ohne Bestätigung; ohne Docker → „nicht installiert". + +## Andockpunkte +- Parser in la_core: `lib/src/parsers/docker_ps.dart` (#68), neu `docker_df.dart`, Probe `docker_stacks`; GTK-Modul „Container" (GTK-Fassung von #68), `widgets.py` +- Fixtures: `test/fixtures/docker_ps_*.txt` (3 Projekte + 2 lose, mit Labels), `docker_system_df_*.txt` + +## Abnahme +- [ ] Fixture mit 3 Projekten und 2 losen Containern wird korrekt gruppiert +- [ ] `up` nutzt Projektverzeichnis und Compose-Datei aus den Labels (argv-Assert) +- [ ] Vorschau nennt Größen vor der Aufräum-Aktion; Aktion nur nach Bestätigung (argv-Assert) +- [ ] Ohne Docker → StatusPage „nicht installiert", kein Fehler + +## Abhängigkeiten +Blockiert durch: #68 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `docker compose` fehlt (nur v1 `docker-compose`) → Operationen ausblenden, Hinweis._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic DI (Teilplan DI4)._ +``` + +--- + +## Issue 21 — [V1.2] DI5 Agenten-Betrieb: Tunnel-Status & Gateway-Audit + +**Meilenstein:** V1.2 · **Labels:** `epic:di`, `tier:n2h`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Job +Wenn ein lokaler Port-Forward zu einem entfernten Dienst stirbt, scheitern Skills still. Ein Blick ins Agenten-Tile soll zeigen, ob die Tunnel leben und was das Agenten-Gateway zuletzt genehmigt hat. Baut auf DI3/#69 (Agenten-Tile: Units, Start-Knöpfe, Budget — gilt für GTK sinngemäß) und A8/#103 (Gateway-Audit) auf. + +## Scope +- Probe `tunnel_status` in la_core: config `tunnels: [{"name": "", "local_port": }]` → `ss -ltnH` (Nutzer) → je Tunnel aktiv/tot; Hand-off „neu starten" = eigene Aktion aus TE4 (argv aus config, keine eingebauten Befehle). +- Audit-Ansicht im Agenten-Tile: letzte genehmigte Aktionen aus dem Gateway-Audit (#103), read-only. +- Grenzen: fremde Backends nie Core; nur `ss`/`systemctl --user`; keine Hosts/Ports im Code (nur config). + +## Andockpunkte +- `packages/la_core/lib/src/probes/tunnel_status.dart`, Parser `ss_listen.dart`; Agenten-Tile aus #69 (GTK-Fassung), Audit-Datei aus #103 +- Fixtures: `test/fixtures/ss_ltn_*.txt`, Audit-Fixture + +## Abnahme +- [ ] Fixture „Port nicht gebunden" → Tunnel rot mit Hand-off; gebunden → grün +- [ ] Audit-Fixture wird gelistet (Zeit, Aktion, Ergebnis), ohne Argumente mit Secrets +- [ ] Grep-Gate: keine Portnummern/Hosts in `la_core`/`prototype/gtk` außerhalb von Fixtures + +## Abhängigkeiten +Blockiert durch: #69, #103 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `ss` fehlt → `/proc/net/tcp` parsen (Fixture vorhanden machen)._ + +_Tier: n2h · Größe: S · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic DI (Teilplan DI5)._ +``` + +--- + +## Issue 22 — [V1.2] ST5 Grafik-Setup-Check, Radar & GNOME-Erweiterungen + +**Meilenstein:** V1.2 · **Labels:** `epic:st`, `tier:qol`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +Treiber- und Kernel-Updates sind die häufigste Ursache für Grafik-Regressionen; GNOME-Updates brechen Erweiterungen. Ein Setup-Check vor und ein Radar nach dem Update halten das sichtbar. Der Radar-Teil baut auf SI3/#82 (Update-Radar) und ST2/#77 (Zeitleiste) auf — gelten für GTK sinngemäß. + +## Scope +- **Teil A, Probe `graphics_setup` (la_core, read-only):** `/sys/module/nvidia_drm/parameters/modeset`, `/proc/driver/nvidia/params` (`PreserveVideoMemoryAllocations`), `/proc/driver/nvidia/version` (Open- vs. proprietäres Modul), `dkms status`, `mokutil --sb-state` (falls vorhanden), Sitzungstyp; **Kernel-Rollback-Bereitschaft:** installierte Kernel (`dpkg-query -W 'linux-image-*'`) vs. `uname -r` → „vorheriger Kernel vorhanden", Sprung in den vorhandenen GRUB-Screen; **GNOME-Erweiterungen:** `gnome-extensions list --enabled` + `gnome-extensions info` → `ERROR`/`OUT_OF_DATE` gelb. +- **Teil B, Radar:** ausstehende Kernel-/NVIDIA-Pakete aus #82 mit Hinweis „nach dem Update ST4 beobachten"; `known_bad_drivers`-Abgleich vor der Installation; Flatpak-Updates (`flatpak remote-ls --updates`); Firmware (`fwupdmgr get-updates --json`, die Installation bleibt extern); Treiber-/Kernel-Wechsel aus #77 im Detail. +- Grenzen: nur Lesen; kein Root; ohne NVIDIA „nicht zutreffend". + +## Andockpunkte +- `packages/la_core/lib/src/probes/graphics_setup.dart`, Parser `dkms_status.dart`, `gnome_extensions.dart`, `flatpak_updates.dart`, `fwupd_updates.dart`; `lib/layouts/grub_config/grub_config.dart` (Flutter-GRUB-Screen als Referenz für die GTK-Fassung) +- Fixtures: `test/fixtures/graphics_setup_*.txt`, `gnome_extensions_*.txt`, `flatpak_updates_*.txt`, `fwupd_*.txt` + +## Abnahme +- [ ] Fixture `modeset=0` → gelb mit Satz; `modeset=1` + Preserve gesetzt → grün +- [ ] Erweiterung `OUT_OF_DATE` in der Fixture → gelb mit Name +- [ ] Fixture „Update auf known-bad-Version" → rot vor der Installation +- [ ] Ohne NVIDIA → „nicht zutreffend"; ohne `fwupdmgr`/`flatpak` → Zeile entfällt + +## Abhängigkeiten +Blockiert durch: #82, #77 (nur Teil B) · ⚡ Teil A sofort startbar · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `fwupdmgr --json` fehlt in der Version → Zeile weglassen, nicht raten._ + +_Tier: QoL · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic ST (Teilplan ST5)._ +``` + +--- + +## Issue 23 — [V1.2] GA2 Steam-Bibliotheken & Platzfresser + +**Meilenstein:** V1.2 · **Labels:** `epic:ga`, `tier:n2h`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +Steam-Bibliotheken auf mehreren Laufwerken, verwaiste Proton-Präfixe und Shader-Caches fressen still Platz. Eine Übersicht mit Vorschau und Papierkorb statt `du -sh` von Hand. Referenz SP2/#84 (Vorschau-Muster). + +## Scope +- Parser in la_core (`lib/src/parsers/vdf.dart`, Mini-VDF/KeyValues): Steam-Wurzeln `~/.steam/steam`, `~/.local/share/Steam`, Flatpak-Pfad `~/.var/app/com.valvesoftware.Steam/data/Steam`; `steamapps/libraryfolders.vdf` → Bibliotheken; je Bibliothek `appmanifest_*.acf` (Name, `SizeOnDisk`, `LastUpdated`), Größen von `compatdata/` und `shadercache/`; verwaiste Verzeichnisse = ohne passendes Manifest. +- Karte „Steam": Bibliotheken mit Mount-Ampel (SP3), Top-N nach Größe, verwaiste Präfixe mit Vorschau → `gio trash ` nach Bestätigung (scheitert auf Fremd-Dateisystemen → Hinweis, **nie `rm`**); Hand-off „Steam öffnen" (WZ3). +- Grenzen: nur Nutzerdateien; kein Root; keine Löschung ohne Bestätigung. + +## Andockpunkte +- `packages/la_core/lib/src/parsers/vdf.dart`, Probe `steam_libraries.dart`; `prototype/gtk/` Karte (DS5) +- Fixtures: `test/fixtures/steam_libraryfolders.txt`, `steam_appmanifest_*.txt`, Verzeichnis-Fixture mit einem verwaisten Präfix + +## Abnahme +- [ ] Fixture-VDF mit zwei Bibliotheken → zwei Karten mit korrekten Pfaden und Spielen +- [ ] Verwaistes `compatdata`-Verzeichnis erkannt; Papierkorb erst nach Bestätigung (argv-Assert `gio trash`) +- [ ] `gio trash`-Fehler (Fremd-Dateisystem) → Hinweis, keine Alternative mit `rm` +- [ ] Ohne Steam → StatusPage „nicht installiert" + +## Abhängigkeiten +Blockiert durch: — (⚡ sofort startbar) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: VDF-Varianten (Escapes, verschachtelte Blöcke) → Parser auf die zwei benötigten Dateien beschränken._ + +_Tier: n2h · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic GA (Teilplan GA2)._ +``` + +--- + +## Issue 24 — [V1.3] UX5 Dashboard anpassbar & Fokus-Modus + +**Meilenstein:** V1.3 · **Labels:** `epic:ux`, `tier:n2h`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +Nicht jede Karte ist jeden Tag wichtig: Karten ein-/ausblenden, umsortieren, und ein Fokus-Modus mit nur Ampel-Leiste und Tages-Check. + +## Scope +- Kartenliste in UX4 (Schalter + Reihenfolge, keine Drag-Pflicht), Layout in `gtk-state.json`. +- Fokus-Modus (Strg+Shift+F): nur Ampel-Leiste (#102) + Tages-Check (LB5). +- 1/2/3 Spalten über `Adw.Breakpoint` (ab 1400 px drei Spalten); Karten aus dem Katalog (DS5). +- Grenzen: nur UI-Zustand. + +## Andockpunkte +- Dashboard-Modul in `mla_app.py`, `widgets.py` (DS5), `settings_page.py` (UX4), `gtk-state.json` (UX1) +- Tests: `prototype/gtk/tests/test_dashboard_layout.py` (xvfb für Breakpoints) + +## Abnahme +- [ ] Ausgeblendete Karte bleibt nach Neustart weg; die Reihenfolge wird gemerkt (State-Datei) +- [ ] Fokus-Modus per Kürzel ein/aus; zeigt nur Ampeln + Tages-Check +- [ ] Drei Spalten ab 1400 px, eine Spalte unter 800 px (xvfb) + +## Abhängigkeiten +Blockiert durch: DS5, UX4 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Breakpoint-Umschaltung flackert → Spaltenzahl nur bei Fenster-Ruhe anpassen._ + +_Tier: n2h · Größe: M · shell:gtk · Gates: `xvfb-run -a python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic UX (Teilplan UX5)._ +``` + +--- + +## Issue 25 — [V1.3] UX6 Wetter-Kachel (Open-Meteo) + +**Meilenstein:** V1.3 · **Labels:** `epic:ux`, `tier:n2h`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Job +Parkliste 4.4 (#88), Mini-Variante mit Bastis Go: eine kleine Wetter-Kachel ohne Geolokalisierung, ohne API-Key, standardmäßig aus. + +## Scope +- Ort manuell: config `weather.lat`/`weather.lon` (Fixtures und Doku nur mit den Beispielkoordinaten aus der Open-Meteo-Dokumentation, nie Werte vom Referenzsystem). +- Abfrage `https://api.open-meteo.com/v1/forecast?latitude=…&longitude=…¤t=temperature_2m,weather_code` über `urllib.request` in einem GLib-Thread; Cache `$XDG_CACHE_HOME/linux-assistant/weather.json`, 1 h; offline → `stale`. +- Standard aus (`weather.enabled = false`); Nutzungsbedingungen (nicht-kommerziell, Attribution „Weather data by Open-Meteo.com") im Issue belegt, Attribution in der Kachel. +- Ausnahme von der Logik-Heimat-Regel: reine Kachel, bleibt in Python. +- Grenzen: kein Netzverkehr bei deaktivierter Kachel; keine Geolokalisierung; keine Dritt-Pakete. + +## Andockpunkte +- `prototype/gtk/weather.py`, Dashboard-Modul, `settings_page.py` (UX4), `states.py` (UX3) +- Tests: `prototype/gtk/tests/test_weather.py` mit gefaktem `urlopen` + +## Abnahme +- [ ] Kachel deaktiviert → kein `urlopen`-Aufruf (Test mit Fake) +- [ ] Fixture-Antwort rendert Temperatur und Symbol zum `weather_code` +- [ ] Attribution sichtbar; der Cache verhindert einen zweiten Aufruf innerhalb einer Stunde (Test mit Fixture-Uhr) + +## Abhängigkeiten +Blockiert durch: — (⚡ sofort startbar) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Nutzungsbedingungen ändern sich → die Kachel zu entfernen ist legitim (n2h-Regel im MANIFEST)._ + +_Tier: n2h · Größe: S · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic UX (Teilplan UX6, Parkliste 4.4)._ +``` + +--- + +## Issue 26 — [V1.3] LB6 ntfy-Ziel im Wächter + +**Meilenstein:** V1.3 · **Labels:** `epic:lb`, `tier:n2h`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Job +Parkliste „Handy-Push" (#88), Mini-Variante mit Bastis Go: Der Wächter darf optional ein ntfy-Ziel benachrichtigen — nur bei Übergängen nach Rot, standardmäßig aus, Token nie im Klartext. Baut auf LB3/#66 und LB4 auf. + +## Scope +- config `ntfy.enabled` (Default false), `ntfy.url`, `ntfy.topic`; Token ausschließlich über den Secret Service (`secret-tool lookup service linux-assistant key ntfy-token`). Keyring gesperrt oder kein Session-Bus → Versand entfällt still (Log-Zeile ohne Token), nie ein Prompt, nie eine Datei (Nie-Liste: kein Klartext-Fallback). +- Nur Übergänge nach Rot (LB4-Entprellung, Ruhezeiten), Ratenbegrenzung 1 Nachricht je Probe und Stunde. +- Versand aus `la_probe` über `packages/la_core/lib/src/io/http_sender.dart` (injizierbar, `dart:io` `HttpClient`); Sendefehler blockieren den Wächter-Lauf nicht. +- „Test senden" in UX4. +- Grenzen: keine Secrets in config/Logs; kein Root. + +## Andockpunkte +- `packages/la_core/bin/la_probe.dart` (Wächter, #66), neu `lib/src/io/http_sender.dart`, `lib/src/notify_ntfy.dart`; UX4 (`settings_page.py`) +- Tests: Fixture-HTTP-Server in `packages/la_core/test/`, Grep-Gate über Logs/config-Fixtures + +## Abnahme +- [ ] Übergang nach Rot → genau ein POST an den Fixture-Server mit Topic und Satz +- [ ] Gesperrter Keyring (Fake liefert Fehler) → kein Versand, Log ohne Token, der Lauf endet grün +- [ ] Der Token taucht weder in Logs noch in `config.json` auf (Grep-Gate) +- [ ] `ntfy.enabled = false` → kein Netzzugriff (der Fake-Sender bleibt ungerufen) + +## Abhängigkeiten +Blockiert durch: #66, LB4 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `secret-tool` fehlt → Funktion deaktiviert mit Hinweis, nie Klartext._ + +_Tier: n2h · Größe: S · shell:gtk · Gates: `cd packages/la_core && dart test`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic LB (Teilplan LB6, Parkliste Handy-Push)._ +``` + +--- + +## Issue 27 — [V1.3] DI6 Odysseus „Ungelesen: N" + +**Meilenstein:** V1.3 · **Labels:** `epic:di`, `tier:n2h`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Job +Parkliste 4.11 (#88), Mini-Variante mit Bastis Go: nur die Zahl ungelesener Nachrichten über die eigene Scoped-API, kein Mail-Client. Nach dem DI3-Muster (#69), Tunnel-Status aus DI5. + +## Scope +- Generisches Schema, kein Wissen über das private Backend im Code: `GET ` mit `Authorization: Bearer ` → JSON `{"unread": }`; `agents.unread.base_url` und `agents.unread.path` aus config; Token aus dem Secret Service (`secret-tool lookup service linux-assistant key odysseus-token`), gesperrt → `stale`. +- Anzeige: Zähler im Agenten-Tile + Hand-off „Im Browser öffnen" (config-URL, WZ3). Tunnel tot (DI5) → `stale` mit Satz. +- Grenzen: nie Inhalte, nie Absender; kein Klartext-Token; fremde Backends nie Core. + +## Andockpunkte +- `packages/la_core/lib/src/probes/unread_count.dart` über `http_sender.dart` (LB6) oder `la_probe --json unread_count`; Agenten-Tile (GTK-Fassung von #69) +- Fixtures: HTTP-Antworten 200/401/Timeout + +## Abnahme +- [ ] Fixture 200 → Zahl im Tile; 401 → `stale` mit Satz; Timeout → `stale` +- [ ] Der Token taucht nirgends außer im Secret Service auf (Grep-Gate) +- [ ] Deaktiviert (keine `base_url`) → kein Netzzugriff + +## Abhängigkeiten +Blockiert durch: #69, DI5 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Die API liefert kein `unread`-Feld → Probe `unknown`, kein Rätselraten über andere Felder._ + +_Tier: n2h · Größe: S · shell:gtk · Gates: `cd packages/la_core && dart test`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic DI (Teilplan DI6, Parkliste 4.11)._ +``` + +--- + +## Issue 28 — [V1.3] ST6 Wochenbericht + +**Meilenstein:** V1.3 · **Labels:** `epic:st`, `tier:n2h`, `size:M`, `shell:gtk` + +### Body + +```markdown +## Job +Einmal pro Woche eine Seite: Boots und Freezes, Updates, Platten-Trend, Backup-Erfolge, Wächter-Übergänge — als Markdown in die Notizen oder den Obsidian-Eingang. Schwärzung wie im Support-Bericht ST3/#78 (gilt für GTK sinngemäß). + +## Scope +- `la_probe --weekly` + User-Units `linux-assistant-weekly.service/.timer` im .deb (Muster #66), Aktivierung per Schalter in UX4. +- Abschnitte: Boots (Dauer, sauber/unsauber, Grafik-Ereignisse — ST4), Updates Kernel/NVIDIA (#82), Platten-Trend (#83), Backup-Erfolge (#63/#101), Wächter-Übergänge (LB4-History). +- Ausgabe `$XDG_STATE_HOME/linux-assistant/reports/.md`; optional Kopie in den Notizordner (WZ2-Muster, Inbox-first); Benachrichtigung „Wochenbericht liegt bereit" (GN3). +- Schwärzung aus ST3 (Hostnamen, IPs, Pfade unter dem Home-Verzeichnis). +- Grenzen: nur Lesen; kein Root; keine Secrets. + +## Andockpunkte +- `packages/la_core/bin/la_probe.dart`, neu `lib/src/report/weekly.dart`, Schwärzung aus #78, Units neben #66 in `build-deb.sh` +- Fixtures: eine Fixture-Woche aus den vorhandenen Probe-Fixtures + +## Abnahme +- [ ] Fixture-Woche → Bericht mit allen fünf Abschnitten +- [ ] Der Bericht enthält keine Hostnamen/IPs/Home-Pfade (Grep-Test) +- [ ] Die Timer-Unit landet im .deb; `systemctl --user list-timers` zeigt sie nach Aktivierung (manuell) + +## Abhängigkeiten +Blockiert durch: #66, ST4, #82, #83, #63, #101 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Einzelne Quellen fehlen → der Abschnitt entfällt mit Hinweis, der Bericht erscheint trotzdem._ + +_Tier: n2h · Größe: M · shell:gtk · Gates: `cd packages/la_core && dart test`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic ST (Teilplan ST6)._ +``` + +--- + +## Issue 29 — [V1.3] DI7 Lab-Status: Container-Labs & VMs + +**Meilenstein:** V1.3 · **Labels:** `epic:di`, `tier:n2h`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Job +Container-basierte Labs (z. B. eine Windows-Umgebung im Container) und VMs laufen über eigene User-Timer; eine Karte zeigt, was läuft und wann der nächste Lauf ist. + +## Scope +- Karte „Lab": Container nach Name-Muster (`lab.container_patterns`, z. B. `["-*"]`) + zugehörige User-Timer (`lab.timer_patterns`) mit nächstem Lauf (`systemctl --user list-timers -o json`); VirtualBox optional (`VBoxManage list runningvms`, wenn vorhanden, sonst entfällt die Zeile). +- Start/Stop nur über User-Units bzw. Docker-CLI nach Bestätigung; keine VM-Innenansicht. +- Grenzen: keine Unit-/Container-Namen im Code (nur config); kein Root. + +## Andockpunkte +- `packages/la_core/lib/src/probes/lab_status.dart` (nutzt Parser aus #67/#68), `prototype/gtk/` Karte (DS5), UX4 (Muster) +- Fixtures: `docker_ps`-Fixture mit Lab-Container, `list-timers`-Fixture, VBox-Fixture + +## Abnahme +- [ ] Fixture mit laufendem Container + Timern → Karte mit nächstem Lauf +- [ ] Ohne Docker/VBox → StatusPage bzw. die Zeile entfällt, kein Fehler +- [ ] Grep-Gate: keine Container-/Unit-Namen außerhalb von config und Fixtures + +## Abhängigkeiten +Blockiert durch: — (⚡ sofort startbar; Parser aus #67/#68 wiederverwenden, sobald vorhanden) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: `VBoxManage` hängt ohne Display → Timeout 3 s, dann „unbekannt"._ + +_Tier: n2h · Größe: S · shell:gtk · Gates: `cd packages/la_core && dart test` · `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic DI (Teilplan DI7)._ +``` + +--- + +## Issue 30 — [V1.3] DI8 Tailscale-Status + +**Meilenstein:** V1.3 · **Labels:** `epic:di`, `tier:n2h`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Job +Ist das Tailnet verbunden und Tailscale-SSH aktiv? Das ist der Rettungsweg vom Handy beim nächsten Desktop-Freeze (ST4). Referenz DI1/#67 (n2h-Interface-Probe; Proton-VPN bleibt generisch dort, Parkliste 4.10 geparkt). + +## Scope +- Probe `tailscale_status` in la_core aus `tailscale status --json` (läuft als Nutzer): `BackendState`, Self online, Peers online/gesamt, Tailscale-SSH aktiv (Feld aus der Fixture bestimmen), Exit-Node aktiv. +- Anzeige **ohne IPs und DNS-Namen** (nur Zähler und Flags — öffentliches Repo, Screenshots); Hand-off „Konsole im Browser" (config-URL, WZ3). +- Grenzen: nur Lesen; kein Root; ohne Binary „nicht installiert". + +## Andockpunkte +- `packages/la_core/lib/src/probes/tailscale_status.dart`, Parser `tailscale_json.dart`; Karte (DS5) +- Fixtures: `test/fixtures/tailscale_status_*.txt` (geschwärzt: keine IPs, keine Hostnamen, keine Keys) + +## Abnahme +- [ ] Fixture-JSON → Peer-Zähler, Backend-Zustand, SSH-Flag +- [ ] Die Ausgabe der Probe enthält keine IP-Adressen und keine DNS-Namen (Regex-Test) +- [ ] Ohne `tailscale` → „nicht installiert"; `BackendState` ≠ Running → gelb mit Satz + +## Abhängigkeiten +Blockiert durch: — (⚡ sofort startbar) · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: JSON-Felder ändern sich zwischen Versionen → nur die genannten Felder lesen, Rest ignorieren._ + +_Tier: n2h · Größe: S · shell:gtk · Gates: `cd packages/la_core && dart test`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic DI (Teilplan DI8)._ +``` + +--- + +## Issue 31 — [V1.3] UX7 Erster Start & Onboarding + +**Meilenstein:** V1.3 · **Labels:** `epic:ux`, `tier:qol`, `size:S`, `shell:gtk`, `gate:manual` + +### Body + +```markdown +## Job +Beim ersten Start wählen, was das Cockpit beobachten soll, den Wächter aktivieren, den Hotkey verstehen — und wissen, dass nichts nach außen telefoniert. + +## Scope +- `Adw.Dialog` beim ersten Start (Marker in `gtk-state.json`): Module/Watchlists wählen (UX4), Wächter aktivieren (optional, #66), Hotkey-Hinweis je Session-Typ (GN2), Datenschutz („keine Telemetrie; Netz nur für aktivierte Kacheln"), „Später"; Wiederaufruf aus UX4. +- Grenzen: nur Konfiguration; Abbruch hinterlässt Standardwerte. + +## Andockpunkte +- `prototype/gtk/onboarding.py`, `mla_app.py` (`do_activate`), `settings_page.py`, DS7 (Texte) +- Tests: `prototype/gtk/tests/test_onboarding.py` (State-Marker, Abbruch-Pfad) + +## Abnahme +- [ ] Der Dialog erscheint genau einmal (State-Test, zweiter Start ohne Dialog) +- [ ] Abbruch hinterlässt die Standardkonfiguration unverändert +- [ ] Dialog hell/dunkel gesichtet (manuell) + +## Abhängigkeiten +Blockiert durch: UX4, GN2, DS7 · Blockiert: _(vom Skript gesetzt)_ +_Risiko & Fallback: Zu viele Schritte → auf zwei Seiten kürzen (Beobachten, Benachrichtigen)._ + +_Tier: QoL · Größe: S · shell:gtk · Gates: `python3 -m unittest discover -s prototype/gtk/tests`._ +_Quelle: Ausbauplan V1.1–V1.3, Epic UX (Teilplan UX7)._ +``` + +--- + +## Issue 32 — [V1.3] Abschluss V1.x — Parkliste, Doku, features.csv + +**Meilenstein:** V1.3 · **Labels:** `roadmap`, `size:S`, `shell:gtk` + +### Body + +```markdown +## Scope +Sammel-Issue zum Abschluss der Wellen V1.1–V1.3. +- Parkliste (#88) aktualisieren: Mini-Varianten aktiv (UX6 Wetter, LB6 ntfy, DI6 Ungelesen); Terminal-Übergabe in WZ3/TE4, Sidebar einklappbar in UX1, Proton-Status generisch in #67/DI8; Dock bleibt geparkt. +- `features.csv`: eine Zeile je V1.x-Funktion (21 Felder, `yes` nur für Zorin OS und GNOME, sonst `?`), geprüft mit dem CSV-Check. +- `docs/mla-next/MILESTONES.md`, Wiki (Roadmap, Admin-Hub, Architecture) und `MANIFEST.md`-Tier-Check nachziehen; `tool/sync-wiki.sh`. +- Doku-Altlasten abräumen oder begründen: `docs/design/admin-hub-followups.md` und `docs/wiki/Roadmap.md` („Issues deaktiviert"), `docs/handoff/README.md`-Statusspalte, `docs/wiki/Release-Process.md` Bump-Beispiel 0.7.2, `docs/handoff/roadmap.md` auf `main` mit veralteten V0.9-Modulen. + +## Abnahme +- [ ] CSV-Check grün; jede V1.x-Funktion hat eine Zeile mit Tier +- [ ] `tool/sync-wiki.sh` (Vorschau) zeigt nur die erwarteten Seiten +- [ ] Parkliste-Tabelle in #88 auf Stand +- [ ] Die vier Doku-Altlasten korrigiert oder je mit Verweis begründet + +## Abhängigkeiten +Blockiert durch: alle V1.x-Teilpläne (zuletzt) · Blockiert: — + +_Größe: S · shell:gtk · Gates: CSV-Check, Leak-Grep._ +_Quelle: Ausbauplan V1.1–V1.3, Abschluss._ +``` + +--- + +## Zusammenfassung (Zuordnungstabelle, angelegt 2026-10-18) + +| # | GitHub | ID | Titel | Meilenstein | Labels | +|---|---|---|---|---|---| +| 1 | #120 | FU4 | Generischer Probe-Vertrag über IPC | V1.1 | epic:fu, tier:core, size:M, shell:gtk | +| 2 | #121 | DS7 | l10n-Grundgerüst GTK | V1.1 | epic:ds, tier:qol, size:S, shell:gtk | +| 3 | #122 | UX1 | Navigation, Fensterzustand & Tastatur-Grundgerüst | V1.1 | epic:ux, tier:qol, size:M, shell:gtk | +| 4 | #123 | UX2 | Kontext-Inspektor (rechte Leiste) | V1.1 | epic:ux, tier:qol, size:M, shell:gtk | +| 5 | #124 | UX3 | Leer-, Lade-, Stale- und Fehlerzustände | V1.1 | epic:ux, tier:qol, size:S, shell:gtk | +| 6 | #125 | UX4 | Einstellungen als Seite | V1.1 | epic:ux, tier:qol, size:M, shell:gtk | +| 7 | #126 | DS5 | Komponenten-Katalog, Galerie & GTK-CI-Gate | V1.1 | epic:ds, tier:qol, size:M, shell:gtk, gate:manual | +| 8 | #127 | LB4 | Ampel-Feinschliff: Bekannt, Snooze, Ruhezeiten, Verlauf | V1.1 | epic:lb, tier:qol, size:M, shell:gtk | +| 9 | #128 | LB5 | Tages-Check „Heute" | V1.1 | epic:lb, tier:qol, size:M, shell:gtk | +| 10 | #129 | ST4 | Grafik-Stabilitäts-Wache (NVIDIA/Wayland) | V1.1 | epic:st, tier:qol, size:M, shell:gtk | +| 11 | #130 | GA1 | Gaming-Vorflug | V1.1 | epic:ga, tier:qol, size:M, shell:gtk | +| 12 | #131 | SP3 | Speicher-Feinschliff: Mounts, Timeshift, SMART | V1.1 | epic:sp, tier:qol, size:M, shell:gtk | +| 13 | #132 | GN4 | App-ID, Desktop-Einbindung & Autostart | V1.2 | epic:gn, tier:qol, size:S, shell:gtk, gate:manual | +| 14 | #133 | GN1 | GNOME-Shell-Suchanbieter | V1.2 | epic:gn, tier:qol, size:M, shell:gtk, gate:manual | +| 15 | #134 | GN2 | Hotkey-Robustheit & Portal-Pfad | V1.2 | epic:gn, tier:qol, size:S, shell:gtk, gate:manual | +| 16 | #135 | GN3 | Benachrichtigungen mit Aktionen & Deep-Links | V1.2 | epic:gn, tier:qol, size:M, shell:gtk, gate:manual | +| 17 | #136 | DS6 | Tastatur, Accessibility & Darstellung folgt GNOME | V1.2 | epic:ds, tier:qol, size:M, shell:gtk, gate:manual | +| 18 | #137 | WZ3 | Werkstatt-Übergaben & Dashboard-Links | V1.2 | epic:wz, tier:qol, size:S, shell:gtk | +| 19 | #138 | TE4 | Palette-Aktionen & Schnellaktionen-Kacheln (GTK) | V1.2 | epic:te, tier:qol, size:M, shell:gtk | +| 20 | #139 | DI4 | Stack-Operationen & Container-Hygiene | V1.2 | epic:di, tier:qol, size:M, shell:gtk | +| 21 | #140 | DI5 | Agenten-Betrieb: Tunnel-Status & Gateway-Audit | V1.2 | epic:di, tier:n2h, size:S, shell:gtk | +| 22 | #141 | ST5 | Grafik-Setup-Check, Radar & GNOME-Erweiterungen | V1.2 | epic:st, tier:qol, size:M, shell:gtk | +| 23 | #142 | GA2 | Steam-Bibliotheken & Platzfresser | V1.2 | epic:ga, tier:n2h, size:M, shell:gtk | +| 24 | #143 | UX5 | Dashboard anpassbar & Fokus-Modus | V1.3 | epic:ux, tier:n2h, size:M, shell:gtk | +| 25 | #144 | UX6 | Wetter-Kachel (Open-Meteo) | V1.3 | epic:ux, tier:n2h, size:S, shell:gtk | +| 26 | #145 | LB6 | ntfy-Ziel im Wächter | V1.3 | epic:lb, tier:n2h, size:S, shell:gtk | +| 27 | #146 | DI6 | Odysseus „Ungelesen: N" | V1.3 | epic:di, tier:n2h, size:S, shell:gtk | +| 28 | #147 | ST6 | Wochenbericht | V1.3 | epic:st, tier:n2h, size:M, shell:gtk | +| 29 | #148 | DI7 | Lab-Status: Container-Labs & VMs | V1.3 | epic:di, tier:n2h, size:S, shell:gtk | +| 30 | #149 | DI8 | Tailscale-Status | V1.3 | epic:di, tier:n2h, size:S, shell:gtk | +| 31 | #150 | UX7 | Erster Start & Onboarding | V1.3 | epic:ux, tier:qol, size:S, shell:gtk, gate:manual | +| 32 | #151 | — | Abschluss V1.x — Parkliste, Doku, features.csv | V1.3 | roadmap, size:S, shell:gtk | diff --git a/meilensteine-v1.1-v1.3.md b/meilensteine-v1.1-v1.3.md new file mode 100644 index 00000000..b2fced3b --- /dev/null +++ b/meilensteine-v1.1-v1.3.md @@ -0,0 +1,36 @@ +# Meilensteine V1.1 – V1.3 — Master Linux Assistant (MLA) + +> Repo: `Toqsick/master-linux-assistant` +> Quelle: Ausbauplan V1.1–V1.3 (Planungssession 2026-10-18, `ausbauplan-v1.1-v1.3.md`) +> Anlegen: fortlaufend ab Nummer 19 (1–11 = V0.8.1–V1.0, 12–18 = MLA-Next 0.0.1–0.4.x), per Skript (siehe Ausbauplan, „Überführung"). Issue-Bodies stehen in `issues-v1.1-v1.3.md`. +> Ziel-Shell: GTK4/libadwaita (`prototype/gtk/`) + Dart-Kern `packages/la_core`. Prämisse: der Flutter-vs-GTK-Entscheid (MLA-Next 0.4.x, #106) fällt für GTK; Rückfallregel im Ausbauplan. + +--- + +## Meilenstein 19 — V1.1 „Cockpit-QoL & UX“ + +**Beschreibung:** + +Komfort auf der GTK-Shell ohne neue System-Nähte (12 Teilpläne): FU4 generischer Probe-Vertrag (`la_probe --json` als Zwischenstufe, `probe.*` über IPC), DS7 l10n-Grundgerüst (gettext, ARB-Keys), UX1 Navigation/Fensterzustand/Kürzel, UX2 Kontext-Inspektor, UX3 Leer-/Lade-/Stale-/Fehlerzustände, UX4 Einstellungen als Seite, DS5 Komponenten-Katalog + Galerie + xvfb-CI-Gate, LB4 Ampel-Feinschliff (Bekannt/Snooze/Ruhezeiten/Verlauf), LB5 Tages-Check „Heute", ST4 Grafik-Stabilitäts-Wache (NVIDIA/Wayland), GA1 Gaming-Vorflug, SP3 Speicher-Feinschliff (Mounts, Timeshift-Soll, SMART ohne Root). Ziel-Shell GTK4/libadwaita + `la_core`; Prämisse: der Flutter-vs-GTK-Entscheid (#106) fällt für GTK. + +**Erfolgskriterium:** Eine neue Probe im Kern erscheint ohne GTK-Codeänderung; ein Neustart stellt Fenster und Modul wieder her; Fixture-Freeze-Boots werden als solche erkannt; eine Timeshift-Zeitplan-Regression wird gelb; jede Fläche hat einen Leer-/Lade-/Stale-/Fehlerzustand; GTK-Widget-Tests laufen in CI unter xvfb. + +--- + +## Meilenstein 20 — V1.2 „Zorin/GNOME-Integration & Werkzeuge“ + +**Beschreibung:** + +GNOME-Nähte und Werkzeuge (11 Teilpläne): GN4 App-ID, Desktop-Actions, AppStream, Autostart (Schritt 0), GN1 GNOME-Shell-Suchanbieter, GN2 Hotkey-Robustheit (gsettings; GNOME 46 hat kein GlobalShortcuts-Portal), GN3 Benachrichtigungen mit Aktionen und Deep-Links, DS6 Tastatur/Accessibility/Darstellung folgt GNOME, WZ3 Werkstatt-Übergaben (Terminal, Dateien, Obsidian, Dashboard-Links), TE4 Palette-Aktionen + Schnellaktionen-Kacheln, DI4 Stack-Operationen + Container-Hygiene, DI5 Tunnel-Status + Gateway-Audit, ST5 Grafik-Setup-Check/Radar/GNOME-Erweiterungen, GA2 Steam-Bibliotheken + Platzfresser. + +**Erfolgskriterium:** Die Super-Taste findet MLA-Module; „Öffnen" einer Benachrichtigung landet im Zielmodul, auch bei gestoppter App; das Dock zeigt die Desktop-Actions; Orca liest Ampeln vor; Compose-Stacks mit Hygiene-Vorschau; zwei Steam-Bibliotheken werden erkannt; jede Übergabe läuft ohne Shell-String (argv-Asserts). + +--- + +## Meilenstein 21 — V1.3 „Nice-to-haves & Parkliste“ + +**Beschreibung:** + +Extras mit kleinem Fußabdruck (9 Teilpläne): UX5 Dashboard anpassbar + Fokus-Modus, UX6 Wetter-Kachel (Open-Meteo), LB6 ntfy-Ziel im Wächter, DI6 Odysseus „Ungelesen: N" (die drei Parkliste-Mini-Varianten mit Bastis Go), ST6 Wochenbericht, DI7 Lab-Status (Container-Labs, VMs), DI8 Tailscale-Status, UX7 Erster Start & Onboarding, Abschluss (Parkliste #88, `features.csv`, Wiki, Doku-Altlasten). + +**Erfolgskriterium:** Deaktivierte Kacheln erzeugen keinen Netzverkehr; Tokens liegen nur im Secret Service (Grep-Gate); der Wochenbericht enthält keine Hostnamen oder IPs; `features.csv`, Wiki und Parkliste sind auf Stand.