Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions docs/handoff/v0.8.2-xdg-browser-abschluss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Abnahme-Nachweis: V0.8.2 „XDG-Browser-Fix"

> **Stand:** 2026-10-23 · **Basis-SHA:** `add73e3` (main, Squash-Merge von PR #118)
> **Betrifft:** GitHub-Issues #33, #34, #47, #52 (WP-B1) · **Meilenstein:** V0.8.2
> **Plan:** `docs/superpowers/plans/2026-10-23-mla-v082-xdg-browser-abschluss.md`
> **Zweck:** Nachweis, dass die Abnahmekriterien der V0.8.2-Issues erfüllt sind — und wo
> der manuelle Rest liegt. Kein Feature-Dokument, sondern ein Abnahmeprotokoll.

## Kurzergebnis

Der XDG-Browser-Fix ist implementiert, in `main` gemerged und durch die globalen CI-Gates
belegt. Es gab **keinen Nachimplementierungs-Bedarf** — die Abnahme gegen `main` ergab, dass
alle Kriterien der vier Issues bereits erfüllt sind. Offen waren nur Nachweis, Tracking-Doku
und der Issue-Abschluss; genau das erledigt dieses Dokument.

| | |
|---|---|
| Code | erfüllt (Squash-Merge `add73e3`, PR #118) |
| Tests | erfüllt (24 Tests in `test/app_launcher_test.dart`, davon 19 neu) |
| Globale CI-Gates | erfüllt (alle 5 grün, siehe Gate-Protokoll) |
| Doku | erfüllt (4 Stellen nachgezogen) |
| Issues | #33, #34, #47, #52 geschlossen |
| **Manueller Abnahmeschritt** | **offen** — braucht einen Desktop (Basti) |

## Abnahme-Kriterien → Beleg

### #33 — TDD: Failing Tests für die XDG-Kette

| Kriterium | Beleg | Status |
|---|---|---|
| `import 'dart:io';` ergänzt | `test/app_launcher_test.dart:1` | erfüllt |
| Gruppen `detectBrowser` / `launchApp` wörtlich | `test/app_launcher_test.dart:55, :270` | erfüllt |
| Gruppe `launchBrowser` ersetzt | `test/app_launcher_test.dart:139` | erfüllt |
| Hermetischer `fake()`-Helper | `test/app_launcher_test.dart:9-50` (`LauncherFake`) | erfüllt |
| `defaultBrowserDesktopId` — 4 Tests | `test/app_launcher_test.dart:81` | erfüllt |
| `desktopEntryExistsIn` — 2 Tests | `test/app_launcher_test.dart:113` | erfüllt |
| `launchBrowser` — 8 Tests laut Spec | `test/app_launcher_test.dart:139`, **9 vorhanden** | erfüllt (Superset) |
| RED-Gate (Compile-Fehler `outputReader`) | historisch, im Branch-Commit `2ef61d5` | erfüllt, überholt durch GREEN |

Die drei Tests über die Spec hinaus: `debug override does not run xdg-settings without an
output reader`, `ignoriert einen Pfad als preferred_browser (WP-B1-Boundary)` und
`übergibt die URL auch an den Binary-Listen-Fallback`.

### #34 — XDG-Stufe in AppLauncher + Snackbar + Doku

| Kriterium | Beleg | Status |
|---|---|---|
| `BrowserLaunchResult`-Enum | `lib/services/app_launcher.dart:6-16` | erfüllt |
| Injizierbare Zugriffe (5) | `app_launcher.dart:46` (`_which`), `:49` (`_starter`), `:53` (`_outputReader`), `:57` (`_desktopEntryExists`), `:61` (`_configuredBrowser`) | erfüllt |
| Reihenfolge preferred → XDG → Liste | `app_launcher.dart:174-207` | erfüllt |
| `.desktop`-Existenzprüfung | `app_launcher.dart:184-198` | erfüllt |
| `gtk-launch` ohne URL / `xdg-open` mit URL | `app_launcher.dart:186-197` | erfüllt |
| Doc-Kommentar exakt 5 Zeilen | `lib/layouts/hub/hub_shell.dart:143-147` | erfüllt |
| Fallback-Info-Snackbar | `hub_shell.dart:154-158` | erfüllt |
| `failed`-Zweig unverändert | `hub_shell.dart:159-162` | erfüllt |
| Doku `docs/wiki/Admin-Hub.md` | `:23-28` | erfüllt |
| Doku `docs/handoff/panels/hub-shell.md` | `:55` | erfüllt |
| Doku `docs/design/feature-spec-admin-hub.md` | `:44-46` | erfüllt |
| Doku `docs/design/admin-hub-followups.md` | `:23` | erfüllt (Checkbox **manuell**, bleibt offen) |

**Härtung über die Spezifikation hinaus:** `_validDesktopId()`
(`app_launcher.dart:216-220`) prüft die von `xdg-settings` gelieferte Desktop-ID gegen
`/`, `..` und ein Namens-Regex (`^[A-Za-z0-9][A-Za-z0-9._-]*\.desktop$`). Das war in
#33/#34 nicht gefordert, schließt aber die Desktop-ID gegen denselben Pfad-Traversal
aus, den WP-B1 für den Config-Wert schließt.

### #47 / #52 (WP-B1) — Boundary-Validation für `preferred_browser`

| Kriterium | Beleg | Status |
|---|---|---|
| trim | `app_launcher.dart:135` | erfüllt |
| Allowlist-Membership (`kKnownBrowsers`) | `app_launcher.dart:137` | erfüllt |
| Pfade (`/`) abgelehnt | `app_launcher.dart:136` | erfüllt |
| `null` → sauberer Fallback | `app_launcher.dart:133-138` | erfüllt |
| Test: validierter Wert | `test/app_launcher_test.dart:252-254` | erfüllt |
| Test: nicht-allowlisteter Name | `test/app_launcher_test.dart:260-263` | erfüllt |
| Test: Pfad-Value | `test/app_launcher_test.dart:265-267` | erfüllt |
| Test: trailing-space-Value | `test/app_launcher_test.dart:256-258` | erfüllt |

Der Allowlist-Check sitzt in `sanitizePreferredBrowser()` und wird an **beiden** Aufrufstellen
angewendet: `detectBrowser()` (`app_launcher.dart:119`) und `launchBrowser()`
(`app_launcher.dart:177`). Damit kann der Config-Wert die eigene Allowlist nicht mehr
umgehen — genau die Lücke, die #47 beschrieben hat.

## Gate-Protokoll

Ausgeführt am 2026-10-23 gegen `add73e3` + die Doku-Commits dieses Branches, mit
**echten Exitcodes** (Fallstrick: `cmd | tail` liefert den Exitcode von `tail` — der
Exitcode wird vor dem Piping gesichert).

| Gate | Kommando | Ergebnis |
|---|---|---|
| Versionsprüfung | `bash tool/check-versions.sh` | OK |
| Format-Gate | `dart format --output=none --set-exit-if-changed lib test` | OK — 123 Dateien, 0 geändert |
| Statische Analyse | `flutter analyze` | OK — *No issues found!* (28,6 s) |
| Dart-Tests | `flutter test` | OK — **223 Tests** |
| Root-Runner-Tests | `(cd additional/python && python3 -m unittest discover -s tests -t .)` | OK — 58 Tests |
| GTK-Token-Gate | `python3 -m unittest discover -s prototype/gtk/tests` | OK — 96 Tests (2 skipped) |
| la_core pub get | `(cd packages/la_core && dart pub get)` | OK |
| la_core Analyse | `(cd packages/la_core && dart analyze)` | OK — *No issues found!* |
| la_core Format | `(cd packages/la_core && dart format --output=none --set-exit-if-changed .)` | OK — 25 Dateien, 0 geändert |
| la_core Tests | `(cd packages/la_core && dart test)` | OK — 68 Tests |

**445 Tests grün.** Reproduzierbar über `bash /workspace/tools/run-mla-gates.sh` bzw.
`python3 /workspace/tools/mla-remote.py gates` (Sandbox-Tooling, nicht Teil des Repos).

### Debian-Build-Gate (Issue #35, V0.8.3 — angrenzend, nicht V0.8.2)

`bash build-deb.sh` benötigt `cmake`, `clang`, `ninja-build`, `libgtk-3-dev`,
`libkeybinder-3.0-dev` und `mesa-utils`. In der Cloud-Sandbox am 2026-10-23
nachinstalliert, danach real ausgeführt:

| Schritt | Ergebnis |
|---|---|
| `bash build-deb.sh` | OK — `Built linux-assistant_0.8.0_amd64.deb` (12,1 MB), Exitcode 0 |
| `dpkg-deb -f linux-assistant.deb Depends` | OK — endet auf `xdg-utils, libgtk-3-bin, libglib2.0-bin` |
| `la_probe --version` (Smoke-Check ohne Display) | OK — `la_probe 0.0.1-spike.1 (dart 3.13.5 …)` |

Damit ist das **Verify-Gate aus Issue #35 bestanden** und #35 ebenfalls schließbar.

## Offene Punkte — manuell, braucht einen Desktop

Diese Punkte sind aus der Cloud-Sandbox **nicht** prüfbar (kein Desktop, kein
Zorin-OS-Referenzsystem). Sie sind bewusst offen gelassen und nicht als erledigt
markiert:

1. **Browser-Werkzeug öffnet Brave Origin ohne Snackbar** (Erfolgskriterium Meilenstein 2).
Automatisch abgesichert: `launchBrowser()` löst `xdg-settings get default-web-browser`
auf und prüft die `.desktop`-Datei, bevor `gtk-launch`/`xdg-open` detached startet
(`app_launcher.dart:184-198`). Auf dem Rechner entscheidet, ob
`brave-origin.desktop` unter den XDG-Datenverzeichnissen liegt.
2. **Checkliste `docs/design/admin-hub-followups.md:19-28`** — fünf manuelle Checks
(Sidebar-Sektion, Browser-Klick, Quick Notes, Dateimanager, Systemmonitor). Die
Browser-Zeile dort bleibt bewusst ungehakt.
3. **Merge des Doku-Branches** — nach Repo-Konvention („kein Push/Merge/Release ohne
Freigabe") bei Basti.

## Aufräumen

`hardening/0.8.x-browser-xdg` ist **superseded**: der Branch steht auf einem
Merge-Stand vom 2026-09-23 (118 Dateien / 13.828 Zeilen hinter `main`) und ist durch
den Squash-Merge in `add73e3` überholt. Er wird nicht weitergeführt. Löschen:

```bash
git push origin --delete hardening/0.8.x-browser-xdg
```

Die Commit-Historie des Branches ist der einzige Ort, an dem die Original-Commit-Nachrichten
(`fix(launcher): start the XDG default browser instead of the first listed binary`,
`fix(launcher): validate preferred_browser against the known-browsers allowlist`,
`fix(python): copy timeshift config with shutil instead of a shell command`,
`fix(python): invoke wget/unzip with argv lists`) als eigene SHAs vorliegen. In `main`
sind sie in `add73e3` zusammengefasst.

## Nicht Teil dieser Abnahme

- **WP-B2** (#45, Icon-Loader-Quote-Shim) — eigens Work-Paket, `gate:manual`/`RV-1`-abhängig.
- **WP-S1/S2/S3** (#49, #44, #42/#50/#46) — V0.8.2.5.
- **WP-P1/P2** (#43, #48) — V0.8.3. Der Code ist in `add73e3` enthalten
(`jfiles.py:181` nutzt `shutil.copy2`, `jessentials.py:172` nutzt eine argv-Liste mit
`--`-Fence), die Issues sind aber weiter offen und gehören nicht in diesen Plan.
- **Issues #53–#57** — V0.8.2.5-Work-Packages, offen.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# SDD-Plan: V0.8.2 „XDG-Browser-Fix" — Abnahme und Abschluss

- **Datum:** 2026-10-23 · **Quellen:** Issue #33, #34, #47, #52 (WP-B1), `AGENTS.md`, `MANIFEST.md`, `meilensteine-v0.8.1-v0.8.5.md` (Meilenstein 2), `security-fixplan-42-50.md` (WP-B1, Verdict-Landkarte), `issues-v0.8.1-v0.8.5.md` (Issue 3/4/5), `lib/services/app_launcher.dart`, `lib/layouts/hub/hub_shell.dart`, `test/app_launcher_test.dart`
- **Branch:** `chore/v0.8.2-xdg-abschluss` · **Basis-SHA:** `add73e3` (main)
- **Freigabe:** Auftrag „arbeite an V0.8.2 – XDG-Browser-Fix weiter und schließe ab" (2026-10-23). Der Code-Teil ist seit `add73e3` (PR #118) gemerged; offen sind Abnahme-Nachweis, Tracking-Doku und das Schliessen der Issues. Merge der Doku-Änderung nach Repo-Konvention weiterhin bei Basti.
- **Nicht-Ziel:** neue Features. Der Plan dokumentiert, verifiziert und schliesst — er implementiert keinen Browser-Code.

## Global Constraints (für jeden Task verbindlich)

1. **Gates je Task frisch ausführen und im Report belegen** — mit *echten* Exitcodes. Fallstrick: `cmd | tail` liefert den Exitcode von `tail`; der Exitcode ist vor dem Piping zu sichern.
2. **Kein Shell-String, kein Root-Exec, keine Secrets in Logs oder Commits.** Die polkit-Trinität (`org.linux-assistant.operations.policy` ↔ `lib/services/linux.dart` `_privilegedEntryPoints` ↔ `build-deb.sh`-chmod) wird nicht berührt.
3. **Beleg- statt Blind-Pflicht:** jede Abnahme-Aussage mit `file:line` oder Gate-Ausgabe unterlegen.
4. Repo-Konventionen: Conventional Commits, deutschsprachige Bodies, Issue-Referenz im Body, `dart format` vor jedem Commit.
5. **Kein Merge ohne Freigabe.** Push und PR-Eröffnung sind Auftrag; der Merge in `main` bleibt bei Basti.
6. Nichts committen, was nicht zum Scope gehört: `.plugin-cache/`, `build/`, `/tmp`-Artefakte, `.deb`-Dateien (laut `AGENTS.md` gitignored).

## Ausgangslage — Abnahme gegen `main` (Basis `add73e3`)

Der XDG-Browser-Fix ist implementiert und in `main` gemerged (PR #118, Squash → `add73e3` „V0.8.x-Hardening: XDG-Browser-Kette (#33/#34, #47)"). Die vier Issues sind trotzdem offen, und die Tracking-Dokumente sind nicht nachgezogen. Die verbleibende Arbeit ist damit **Nachweis + Doku + Issue-Abschluss**, nicht Implementierung.

| Kriterium | Quelle | Beleg in `main` | Status |
|---|---|---|---|
| `import 'dart:io';` ergänzt | #33 | `test/app_launcher_test.dart:1` | erfüllt |
| Gruppe `defaultBrowserDesktopId` (4 Tests) | #33 | `test/app_launcher_test.dart:81` | erfüllt |
| Gruppe `desktopEntryExistsIn` (2 Tests) | #33 | `test/app_launcher_test.dart:113` | erfüllt |
| Gruppe `launchBrowser` (8 Tests, Spec) | #33 | `test/app_launcher_test.dart:139` — 9 Tests (Superset: + Debug-Override, + WP-B1-Pfad, + URL am Listen-Fallback) | erfüllt |
| Gruppen `detectBrowser` / `launchApp` wörtlich | #33 | `test/app_launcher_test.dart:55, :270` | erfüllt |
| `BrowserLaunchResult`-Enum | #34 | `lib/services/app_launcher.dart:6-16` | erfüllt |
| 5 injizierbare Zugriffe | #34 | `app_launcher.dart:46, :49, :53, :57, :61` | erfüllt |
| Reihenfolge preferred → XDG → `kKnownBrowsers` | #34 | `app_launcher.dart:174-207` | erfüllt |
| Doc-Kommentar exakt 5 Zeilen | #34 | `lib/layouts/hub/hub_shell.dart:143-147` | erfüllt |
| Fallback-Info-Snackbar | #34 | `hub_shell.dart:154-158` | erfüllt |
| Doku `Admin-Hub.md` | #34 | `docs/wiki/Admin-Hub.md:23-28` | erfüllt |
| Doku `hub-shell.md` | #34 | `docs/handoff/panels/hub-shell.md:55` | erfüllt |
| Doku `feature-spec-admin-hub.md` | #34 | `docs/design/feature-spec-admin-hub.md:44-46` | erfüllt |
| Doku `admin-hub-followups.md` | #34 | `docs/design/admin-hub-followups.md:23` | erfüllt (Checkliste manuell — **bleibt offen**) |
| trim + Allowlist + `/` ablehnen | #47 / #52 | `app_launcher.dart:133-138` (`sanitizePreferredBrowser`) | erfüllt |
| 4 Config-Pfad-Tests | #52 | `test/app_launcher_test.dart:251-268` | erfüllt |

**Erschwerend: `_validDesktopId` (app_launcher.dart:216-220) prüft die Desktop-ID zusätzlich gegen `/`, `..` und ein Namens-Regex — Härtung über die Issue-Spezifikation hinaus.**

## Design-Entscheidungen

1. **Kein Nachimplementieren.** Die Abnahme prüft den Ist-Stand gegen die Issue-Kriterien und schliesst nur ab, was belegbar erfüllt ist. Kein Refactoring von `app_launcher.dart` — die Datei ist frisch gemerged, das erhöht nur das Diff-Risiko ohne Nutzen.
2. **Checkboxen nur bei objektiv erfüllten Kriterien.** `admin-hub-followups.md:23` ist eine *manuelle* GUI-Checkliste (Sidebar, Browser-Klick, Quick Notes, Dateimanager, Systemmonitor). Ohne Bastis Rechner wird sie **nicht** abgehakt — sie ist der sichtbare Rest des Meilensteins.
3. **Issue-Schließung mit Evidenz, nicht mit Behauptung.** Jedes geschlossene Issue bekommt einen Kommentar mit `file:line`-Belegen und den Gate-Ausgaben. #34 wird geschlossen, weil alle drei Gates in seinem Body erfüllt sind; der manuelle Meilenstein-Abnahmeschritt (Brave Origin auf Zorin 18.1) wird im Meilenstein-Doc **sichtbar offen** gehalten und in PR + Issue-Kommentar ausdrücklich genannt.
4. **Der veraltete Branch `hardening/0.8.x-browser-xdg` wird nicht fortgeführt.** Er steht auf einem Merge-Stand vom 2026-09-23 (118 Dateien / 13.828 Zeilen hinter `main`) und ist durch den Squash-Merge in `add73e3` überholt. Er wird nur noch zur Löschung vorgemerkt — er ist superseded, kein Arbeitsstand.
5. **Commit-Aufteilung nach Doc-Art:** ein Commit für die Tracking-Doku, einer für den Nachweis. Getrennt, damit der Nachweis später nicht die Doku-Historie verdeckt.

## Tasks

- **Task 0 — Prep:** Branch `chore/v0.8.2-xdg-abschluss` von `main` (`add73e3`), Git-Identität `ElGregor <Bastick123@gmail.com>` (aus der Historie übernommen), Plan-Datei. Commit `docs(mla): SDD-Plan V0.8.2 XDG-Browser-Fix — Abnahme und Abschluss`.
- **Task 1 — Tracking-Doku:** `issues-v0.8.1-v0.8.5.md` (Issue 3/4-Checkboxen), `meilensteine-v0.8.1-v0.8.5.md` (Meilenstein 2: Codeseitig erfüllt + manueller Rest offen), `security-fixplan-42-50.md` (globale CI-Gates, WP-B1 als erledigt). Commit `docs(mla): V0.8.2 XDG-Browser-Fix — Tracking-Doku nachziehen`.
- **Task 2 — Nachweis-Doc:** `docs/handoff/v0.8.2-xdg-browser-abschluss.md` (neu, deutsch, `file:line`-evidenced nach `docs/handoff/`-Konvention): Kriterien-Tabelle, Gate-Protokoll, manueller Rest, Löschhinweis für den superseded Branch. Commit `docs(mla): Abnahme-Nachweis V0.8.2 XDG-Browser-Fix`.
- **Task 3 — Gates:** alle globalen Gates frisch mit echten Exitcodes: `tool/check-versions.sh`, `dart format --output=none --set-exit-if-changed lib test`, `flutter analyze`, `flutter test`, Python-Unit-Tests (`additional/python`, `prototype/gtk/tests`), `packages/la_core`-Gates. Optional, falls Build-Deps installierbar: `bash build-deb.sh` + `dpkg-deb -f linux-assistant.deb Depends` (Issue #35 Verify-Gate). Ergebnis in Task 2 nachtragen, falls nachträglich erzeugt.
- **Task 4 — Push + PR:** Branch über die Contents-API-Bridge publizieren, PR gegen `main` eröffnen. Body: Summary, Abnahme-Tabelle, Gate-Protokoll, manueller Rest, Merge-Hinweis.
- **Task 5 — Issues schließen:** #33, #34, #47, #52 mit Evidenz-Kommentar schließen; #35 nur, wenn das Build-Gate real grün läuft. Offen bleiben: #43, #48, #53–#56 (andere Work-Packages, nicht Teil dieses Plans).

## Boundary / bewusst draußen

- **Manueller Abnahmeschritt (Basti):** Browser-Werkzeug öffnet Brave Origin (Zorin 18.1) ohne Snackbar, plus die übrigen Checks in `docs/design/admin-hub-followups.md`. Ohne Desktop-Umgebung in dieser Sandbox nicht prüfbar.
- **Keine Implementierungsänderung** an `lib/services/app_launcher.dart`, `lib/layouts/hub/hub_shell.dart` oder `test/app_launcher_test.dart` — reine Abnahme.
- **Keine Version-Erhöhung, kein Tag, kein Release.** `version` bleibt `0.8.0`; Release-Entscheidung liegt bei Basti (`docs/wiki/Release-Process.md`).
- **Kein Merge in `main`** ohne Freigabe.
- **Kein Löschen von Branches** — die App hat kein Branch-Delete-Tool; `hardening/0.8.x-browser-xdg` wird zur manuellen Löschung gemeldet.

## Risiken

| Risiko | Gegenmaßage |
|---|---|
| Checkboxen werden getickt, ohne dass das Gate lief | Gate-Exitcodes im Nachweis-Doc festhalten, Ticks erst nach grünem Lauf |
| `flutter test` schlägt nach Doku-Änderung fehl | Doku-Änderungen liegen außerhalb `lib/`/`test/` — Format-Gate prüft nur `lib test` |
| Squash-Merge-Vorlage verhindert Commit-Nachweis | im Nachweis beide SHAs nennen: Original-Branch-Commit (`388811d`) und Squash in `main` (`add73e3`) |
| Manuelle Checks verschwinden unbemerkt | Meilenstein-Doc führt den manuellen Rest als offenen Punkt mit Zeilenangabe |
17 changes: 17 additions & 0 deletions issues-v0.8.1-v0.8.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ _Quelle: Implementierungsplan V0.8.X, Task 1._

**Meilenstein:** V0.8.2 · **Labels:** `bug`, `xdg-launcher`, `gate:test`

**Status (2026-10-23):** erledigt — Tests in `test/app_launcher_test.dart`
(`defaultBrowserDesktopId` 4, `desktopEntryExistsIn` 2, `launchBrowser` 9), Squash-Merge
`add73e3`. GitHub-Issue #33 geschlossen. Nachweis:
`docs/handoff/v0.8.2-xdg-browser-abschluss.md`.

### Body

```markdown
Expand All @@ -94,6 +99,13 @@ _Quelle: Implementierungsplan V0.8.X, Task 2 (Steps 1–2)._

**Meilenstein:** V0.8.2 · **Labels:** `bug`, `xdg-launcher`, `gate:test`, `gate:verify`

**Status (2026-10-23):** erledigt — `app_launcher.dart` (XDG-Stufe, 5 injizierbare Zugriffe),
`hub_shell.dart` (5-Zeilen-Doc-Kommentar, Fallback-Snackbar), vier Doku-Stellen
(`Admin-Hub.md`, `hub-shell.md`, `feature-spec-admin-hub.md`, `admin-hub-followups.md`),
Squash-Merge `add73e3`. GitHub-Issue #34 geschlossen. Der manuelle Abnahmeschritt
(Brave Origin auf Zorin 18.1) bleibt offen — siehe Meilenstein 2 in
`meilensteine-v0.8.1-v0.8.5.md`.

### Body

```markdown
Expand Down Expand Up @@ -123,6 +135,11 @@ _Quelle: Implementierungsplan V0.8.X, Task 2 (Steps 3–8)._

**Meilenstein:** V0.8.3 · **Labels:** `packaging`, `xdg-launcher`, `gate:verify`

**Status (2026-10-23):** erledigt — `deb/DEBIAN/control:2` trägt die drei Pakete, Verify-Gate
real gelaufen: `bash build-deb.sh` → `Built linux-assistant_0.8.0_amd64.deb`, `dpkg-deb -f …
Depends` endet auf `xdg-utils, libgtk-3-bin, libglib2.0-bin`. GitHub-Issue #35 geschlossen.
Nachweis: `docs/handoff/v0.8.2-xdg-browser-abschluss.md`.

### Body

```markdown
Expand Down
Loading
Loading