Files
tessera-ctl/.planning/phases/18-desktop-client-fertigstellen/18-05-PLAN.md
T

20 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, user_setup, estimate, must_haves
phase plan type wave depends_on files_modified autonomous requirements user_setup estimate must_haves
18-desktop-client-fertigstellen 05 execute 3
18-02
18-04
.gitea/workflows/ci.yml
.gitea/scripts/desktop-collect.sh
.gitea/scripts/publish-release.sh
apps/desktop/src-tauri/Cargo.toml
apps/desktop/src-tauri/Cargo.lock
false
DESK-01
DESK-04
DESK-05
tokens raw_tokens tasks confidence
70000 70000 3 low
truths artifacts key_links
Der CI-Job desktop baut auf dem Linux-Runner zusaetzlich den Windows-Installer per Cross-Bau (cargo-xwin, NSIS) und sammelt `Tessera-Setup-X.Y.Z.exe` neben `Tessera-X.Y.Z.AppImage` ein; das Manifest traegt beide Plattformen (D-04, D-05).
Ein Push auf main endet mit einem gruenen Lauf: Job desktop mit beiden Dateien, Job publish mit Abbildern, die die Pakete tragen (D-06, D-08).
Jeder Fehlschlag der Pipeline wird gelesen, der Job angepasst, erneut gepusht — hoechstens drei Runden, jede als normaler Commit (D-16).
path provides contains
.gitea/workflows/ci.yml Windows-Cross-Bau-Schritte im Job desktop, Einsammeln mit --require linux,windows cargo-xwin
from to via pattern
.gitea/workflows/ci.yml (Schritt Windows NSIS Cross-Bau) .gitea/scripts/desktop-collect.sh Bundle-Verzeichnis target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe -> Tessera-Setup-X.Y.Z.exe x86_64-pc-windows-msvc
from to via pattern
.gitea/workflows/ci.yml (desktop) .gitea/workflows/ci.yml (publish) actions/cache Schluessel desktop-dist-${{ gitea.sha }} (aus 18-02) desktop-dist-
Der Windows-Installer entsteht im selben CI-Job wie das AppImage — als Cross-Bau auf dem Linux-Runner (`cargo tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles nsis`, NSIS aus dem Ubuntu-Paket). Weil dieser Bau nur in der Pipeline beweisbar ist (kein Windows-Werkzeug auf dem Entwicklungsrechner, siehe RESEARCH), enthaelt der Plan die in D-16 vorgesehene Iterationsschleife: pushen, Protokoll lesen, Job anpassen, erneut pushen — hoechstens drei Runden.

Purpose: D-04, D-05, D-06 und D-16 aus 18-CONTEXT.md; Erfolgskriterium 1 (bis auf den Release-Anhang, der erst beim naechsten Freigabe-Tag sichtbar wird — der Upload-Pfad selbst ist in 18-02 gebaut und per Probelauf geprueft). Output: Erweiterter Job desktop, gruener Pipeline-Lauf mit beiden Dateien, Beta-Abbilder mit Paketen.

Rollen: Der Executor pusht nie. Der Orchestrator pusht (git push; die Push-Adresse zeigt auf localhost:3002), beobachtet den Lauf in Gitea und meldet Status und Protokollauszug zurueck. Der Executor liest, behebt, committet.

Artifacts this phase produces

Dieser Plan: .gitea/workflows/ci.yml (Schritte "Windows-Werkzeuge", "Windows-Installer bauen (Cross-Bau)", erweiterte Cache-Pfade, Einsammeln mit --require linux,windows); bei Bedarf Korrekturen an .gitea/scripts/desktop-collect.sh, .gitea/scripts/publish-release.sh (GITEA_API-Umgehung) und apps/desktop/src-tauri/Cargo.toml (rustls-tls-Ausweichlösung). Gesamtliste der Phase: siehe 18-01-PLAN.md.

<execution_context> @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/18-desktop-client-fertigstellen/18-CONTEXT.md @.planning/phases/18-desktop-client-fertigstellen/18-RESEARCH.md @.planning/phases/18-desktop-client-fertigstellen/18-01-SUMMARY.md @.planning/phases/18-desktop-client-fertigstellen/18-02-SUMMARY.md @.planning/phases/18-desktop-client-fertigstellen/18-04-SUMMARY.md

@.gitea/workflows/ci.yml @.gitea/scripts/desktop-collect.sh @.gitea/scripts/publish-release.sh @apps/desktop/src-tauri/Cargo.toml

Task 1: Windows-Cross-Bau in den Job desktop einbauen Reine Workflow-Schritte; Rueckbau ist ein Commit, kein Zustand ausserhalb des Runners ausser dem Cache. .gitea/workflows/ci.yml .gitea/workflows/ci.yml (Job desktop aus 18-02), .gitea/scripts/desktop-collect.sh (Windows-Zweig: Bundle-Pfad und Zielname), .planning/phases/18-desktop-client-fertigstellen/18-RESEARCH.md (Abschnitte "Pattern 1", "Standard Stack: Installation", "Common Pitfalls 2-5", "Open Questions 2-3"), .planning/phases/18-desktop-client-fertigstellen/18-CONTEXT.md (Abschnitt "Specific Ideas": Runner 8 Kerne/15 GB, nacheinander im selben Job) Im Job `desktop` (Datei `.gitea/workflows/ci.yml`) folgende Aenderungen, Schrittnamen deutsch:
  1. Schritt "Systemabhaengigkeiten": die apt-Liste um lld llvm clang nsis erweitern (alle vier am 2026-09-16 im Runner-Abbild per apt-cache policy bestaetigt: lld/llvm/clang 18, nsis 3.09).
  2. Neuer Schritt "Windows-Werkzeuge" nach "Rust-Toolchain": rustup target add x86_64-pc-windows-msvc und command -v cargo-xwin >/dev/null 2>&1 || cargo install --locked cargo-xwin (Legitimitaetspruefung in RESEARCH: OK, rust-cross/cargo-xwin, 0.23.1).
  3. Schritt "Cargo-Zwischenspeicher": path um ~/.cargo/bin/cargo-xwin, ~/.cache/cargo-xwin (Windows-SDK-Ablage von cargo-xwin, mehrere hundert MB, soll nur einmal geladen werden) und ~/.local/share/tauri (NSIS-Plugins, die der Tauri-Bundler beim ersten Windows-Bau laedt) erweitern.
  4. Schritt "Alte Bundles entfernen": zusaetzlich rm -rf apps/desktop/src-tauri/target/x86_64-pc-windows-msvc/release/bundle.
  5. Neuer Schritt "Windows-Installer bauen (Cross-Bau)" nach dem AppImage-Schritt (nacheinander, ein Job, ein Cache — CONTEXT "Specific Ideas"): pnpm --filter @tessera/desktop exec tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles nsis.
  6. Schritt "Pakete einsammeln": --require linux,windows.
  7. Kopfkommentar des Jobs: zwei Saetze zum Cross-Bau und zum Grund, warum die Version rein numerisch bleibt (Pitfall 2).

Keine -j-Begrenzung und keine CARGO_BUILD_JOBS-Vorgabe im ersten Anlauf; beides ist eine Ausweichlösung der Schleife (Task 3), falls der Runner den Speicher ausschoepft. desktop-collect.sh braucht keine Aenderung, wenn der Windows-Zweig aus 18-01 (desktop-collect.sh) den Pfad target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe bereits kennt — pruefen, sonst nachziehen. <acceptance_criteria> - grep -c 'cargo-xwin' .gitea/workflows/ci.yml ergibt mindestens 3 (Install, Cache-Pfad, Bauschritt). - grep -c -- '--target x86_64-pc-windows-msvc --bundles nsis' .gitea/workflows/ci.yml ergibt 1. - grep -c 'desktop-collect.sh --require linux,windows' .gitea/workflows/ci.yml ergibt 1; grep -c 'desktop-collect.sh --require linux$' .gitea/workflows/ci.yml ergibt 0. - Die apt-Zeile enthaelt nsis, lld, llvm und clang (grep -E 'lld llvm clang nsis|nsis' .gitea/workflows/ci.yml). - grep -c 'x86_64-pc-windows-msvc/release/bundle/nsis' .gitea/scripts/desktop-collect.sh ergibt mindestens 1. - sh -n .gitea/scripts/desktop-collect.sh endet mit 0. </acceptance_criteria> cd /home/vicolab/projects/tessera-ctl && test "$(grep -c 'cargo-xwin' .gitea/workflows/ci.yml)" -ge 3 && grep -q -- '--target x86_64-pc-windows-msvc --bundles nsis' .gitea/workflows/ci.yml && grep -q 'desktop-collect.sh --require linux,windows' .gitea/workflows/ci.yml && grep -q 'nsis' .gitea/workflows/ci.yml && grep -q 'x86_64-pc-windows-msvc/release/bundle/nsis' .gitea/scripts/desktop-collect.sh && sh -n .gitea/scripts/desktop-collect.sh && node -e "const y=require('fs').readFileSync('.gitea/workflows/ci.yml','utf8');const d=y.indexOf('\n desktop:'),p=y.indexOf('\n publish:');if(d===-1||p===-1||d>p)process.exit(1);const job=y.slice(d,p);if(job.indexOf('--bundles appimage')>job.indexOf('--bundles nsis'))process.exit(2)" && echo WINDOWS-STEPS-OK <fails_when>Ein Kennzeichen fehlt, das Einsammeln fordert Windows nicht, das Sammel-Skript kennt den NSIS-Pfad nicht, oder der Windows-Schritt steht vor dem AppImage-Schritt (Exit 2) — WINDOWS-STEPS-OK fehlt.</fails_when> Der Job desktop installiert Windows-Werkzeuge, baut nach dem AppImage den NSIS-Installer per Cross-Bau, cached SDK und NSIS-Plugins und sammelt beide Dateien ein. Commit liegt bereit fuer den Push.

Task 2: Push und CI-Lauf beobachten — gruen mit beiden Dateien? Der Commit aus Task 1 (bzw. aus der letzten Runde von Task 3) liegt lokal auf `main`; der Gitea-Runner `gitea-runner` laeuft (Container aktiv), das Secret `REGISTRY_TOKEN` ist gesetzt. Den Stand nach Gitea pushen und den Pipeline-Lauf "Tessera CI/CD" beobachten — der Executor darf nicht pushen (Projektregel: Push nur durch Orchestrator/Nutzer, Push-Adresse zeigt dauerhaft auf `localhost:3002`, nie ueber `git.vicolab.de`). Der Executor hat den Job `desktop` um den Windows-Cross-Bau erweitert, Skripte und Workflow statisch geprueft und committet. Was jetzt nur der Orchestrator kann: `git push` auf `main` und den Lauf in Gitea verfolgen (Gitea-MCP oder Weboberflaeche). Der erste Lauf dauert deutlich laenger als bisher (Rust-Toolchain, zwei Release-Baue, Windows-SDK-Download); erst mit warmem Cache sinkt die Zeit.

Zurueckmelden — je nach Ausgang:

Gruen: Aus dem Job desktop, Schritt "Pakete einsammeln", die beiden Ausgabezeilen (windows: Tessera-Setup-1.1.0-beta.{sha}.exe (…) und linux: Tessera-1.1.0-beta.{sha}.AppImage (…)), dazu Status des Jobs publish (gruen) und die Zeile mit den gepushten Etiketten.

Rot: Name des gescheiterten Jobs und Schritts sowie die letzten rund 60 Protokollzeilen dieses Schritts (mit der eigentlichen Fehlermeldung — bei Rust die Zeilen ab error: bzw. error[E…], bei apt die Zeile E:, bei curl den HTTP-Code und die Antwort). Diese Runde zaehlt (Runde 1 von hoechstens 3). Der Lauf ist gruen; Schritt "Pakete einsammeln" nennt genau eine .exe und genau ein .AppImage; der Job publish hat die Beta-Abbilder gepusht. Der Executor prueft nach der Rueckmeldung zusaetzlich git status --porcelain (leer) und dass git log -1 --format=%H dem vom Orchestrator genannten Lauf-Commit entspricht. Antworte mit "gruen" plus den beiden Dateizeilen — oder mit "rot" plus Job, Schritt und Protokollauszug. Der Lauf ist gruen; Schritt "Pakete einsammeln" nennt genau eine .exe und genau ein .AppImage; der Job publish hat die Beta-Abbilder gepusht. Rueckmeldung liegt vor. Bei "gruen" ist der Plan fertig (Task 3 entfaellt). Bei "rot" geht es mit Task 3 weiter.

Task 3: Iterationsschleife — Fehler lesen, Job anpassen, erneut pushen (hoechstens drei Runden) .gitea/workflows/ci.yml, .gitea/scripts/desktop-collect.sh, .gitea/scripts/publish-release.sh, apps/desktop/src-tauri/Cargo.toml, apps/desktop/src-tauri/Cargo.lock Der vom Orchestrator gelieferte Protokollauszug, .planning/phases/18-desktop-client-fertigstellen/18-RESEARCH.md (Abschnitte "Common Pitfalls 1-5", "Open Questions", "Assumptions Log"), .gitea/workflows/ci.yml, .gitea/scripts/desktop-collect.sh Nur ausfuehren, wenn Task 2 "rot" gemeldet hat. Je Runde: Ursache aus dem Protokoll bestimmen, **eine** gezielte Aenderung machen, lokal pruefen (`sh -n` fuer Skripte, `cargo check` bei Cargo-Aenderungen), committen mit `ci(desktop): Runde N — {Ursache in fuenf Woertern}`, dann zurueck zu Task 2 (der Orchestrator pusht und meldet). Nach der dritten roten Runde **stoppen** und dem Nutzer den Stand mit dem letzten Protokollauszug vorlegen (kein vierter Versuch ohne Ruecksprache).

Bekannte Fehlerbilder und die jeweils vorgesehene Aenderung (in dieser Reihenfolge pruefen):

Signatur im Protokoll Ursache Aenderung
E: Unable to locate package … Paketname falsch/umbenannt Namen mit docker run --rm gitea/runner-images:ubuntu-latest sh -c 'apt-get update -qq; apt-cache policy {name}' pruefen und in der apt-Zeile korrigieren
The system library '…' required by crate '…' was not found (pkg-config) dev-Paket fehlt fehlendes lib…-dev in die apt-Zeile aufnehmen (Pitfall 5)
failed to run custom build command for openssl-sys beim Ziel x86_64-pc-windows-msvc TLS-Backend zieht OpenSSL fuer das Windows-Ziel in Cargo.toml reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } (Pitfall 3), lokal cargo check, Cargo.lock mit committen
makensis: not found / NSIS … not installed NSIS fehlt im PATH nsis in der apt-Zeile pruefen; sonst Pfad /usr/bin/makensis per which makensis im Protokoll ausgeben lassen
failed to download NSIS plugin / nsis_tauri_utils Netz/GitHub gleicher Stand, erneut pushen (leerer Commit ci(desktop): Runde N — erneuter Lauf)
llvm-rc / rc.exe / winres / embed-resource Ressourcen-Compiler nicht gefunden sudo ln -sf /usr/bin/llvm-rc-18 /usr/bin/llvm-rc im Schritt "Windows-Werkzeuge" oder env: RC: llvm-rc-18 am Bauschritt
xwin / Failed to download / manifest beim ersten Cross-Bau Windows-SDK-Download erneut pushen; falls wiederholt: env: XWIN_ARCH: x86_64 und XWIN_CACHE_DIR: ${{ github.workspace }}/.xwin-cache (dann .xwin-cache in die Cache-Pfade)
optional build metadata in app version must be numeric-only Version nicht numerisch git describe-Ausgabe im Protokoll pruefen; desktop-version.sh haette abbrechen muessen — Regex im Skript nachziehen
genau eine Datei erwartet (Sammel-Skript) Bundle-Pfad oder Altbestand Pfad mit find apps/desktop/src-tauri/target -name '*.exe' -path '*bundle*' im Protokoll ermitteln und im Skript anpassen; Aufraeum-Schritt pruefen
Cache service responded with 4xx/5xx / Failed to save / fail-on-cache-miss obwohl gespeichert actions/cache-Version vs. Cache-Server actions/cache/save@v3 und actions/cache/restore@v3 (Open Question 3); bleibt es rot: target aus den Cache-Pfaden nehmen (zu gross)
Killed / signal: 9 / memory waehrend rustc Speicher env: CARGO_BUILD_JOBS: 4 am Job (CONTEXT "Specific Ideas")
Job-Zeitueberschreitung Baudauer plus Cache-Sicherung target aus den Cache-Pfaden nehmen, ~/.cargo/registry und xwin-Ablage behalten
docker build scheitert an COPY desktop-dist Verzeichnis fehlt im Kontext Cache-Restore-Pfad und test -f-Schritt in publish pruefen
Release-Upload 413 (nur bei Tags) Proxy-Groessengrenze vor git.vicolab.de am Release-Schritt env: GITEA_API: http://172.18.0.1:3002/api/v1 (Host-Adresse, ueber die der Runner auch seinen Cache-Server erreicht)
Release-Upload 400 mit "file type" (nur bei Tags) Gitea [repository.release] ALLOWED_TYPES eingeschraenkt nicht im Repository loesbar — dem Nutzer melden (Server-Einstellung); Voreinstellung der Instanz laesst alle Typen zu (geprueft 2026-09-16)
cargo clippy Fehler Code Stelle beheben, cargo clippy lokal gruen

Jede Runde im SUMMARY festhalten: Signatur, Ursache, Aenderung, Commit. Trifft keine Signatur zu, die Ursache aus dem Protokoll ableiten und die kleinste plausible Aenderung waehlen; im Zweifel zuerst mehr Protokoll anfordern (z. B. RUST_BACKTRACE=1 oder --verbose am Bauschritt), statt zu raten. <acceptance_criteria> - Jede Runde ist genau ein Commit mit Praefix ci(desktop): Runde N — (git log --oneline -5 | grep -c 'ci(desktop): Runde' entspricht der Rundenzahl). - Nach jeder Aenderung: sh -n fuer geaenderte Skripte endet mit 0; bei Cargo-Aenderungen endet cargo check in apps/desktop/src-tauri mit 0. - Es gibt nie mehr als drei Runden; nach der dritten roten Runde wird der Stand dem Nutzer vorgelegt statt weiter zu pushen. - Am Ende: Task 2 hat "gruen" mit genau einer .exe- und einer .AppImage-Zeile gemeldet. </acceptance_criteria> cd /home/vicolab/projects/tessera-ctl && sh -n .gitea/scripts/desktop-collect.sh && sh -n .gitea/scripts/publish-release.sh && sh -n .gitea/scripts/desktop-version.sh && (cd apps/desktop/src-tauri && cargo check 2>&1 | tail -1 | grep -q Finished) && test "$(git rev-list --count --grep='ci(desktop): Runde' HEAD~6..HEAD)" -le 3 && echo ROUND-OK <fails_when>Ein Skript hat einen Syntaxfehler, cargo check scheitert nach einer Cargo-Aenderung, oder es gibt mehr als drei Runden-Commits — ROUND-OK fehlt.</fails_when> Der Orchestrator bestaetigt nach der letzten Runde einen gruenen Lauf mit beiden Dateizeilen (Task 2). Ein gruener Pipeline-Lauf auf main mit Tessera-Setup-1.1.0-beta.{sha}.exe und Tessera-1.1.0-beta.{sha}.AppImage im Schritt "Pakete einsammeln" und gruenem publish; hoechstens drei dokumentierte Runden.

<threat_model>

Trust Boundaries

Boundary Description
Runner -> Internet (rustup, crates.io, Microsoft-SDK ueber xwin, NSIS-Plugins von GitHub) Der Cross-Bau laedt Werkzeuge und das Windows-SDK aus dem Netz.
Runner-Cache -> Bau Aus dem Cache wiederhergestellte Artefakte (SDK, target/) fliessen in das Paket ein.
Gebautes .exe -> Anwender-PC Unsigniert; SmartScreen warnt (D-09).

STRIDE Threat Register

Threat ID Category Component Severity Disposition Mitigation Plan
T-18-15 Tampering Werkzeugketten-Download (cargo-xwin, SDK, NSIS-Plugins) medium mitigate cargo install --locked cargo-xwin (Lockfile des Werkzeugs), rustup von der offiziellen Adresse, SDK ueber cargo-xwin (prueft Microsoft-Manifest-Hashes), NSIS aus dem Ubuntu-Archiv; Tauri laedt seine NSIS-Plugins mit hinterlegten Pruefsummen.
T-18-16 Tampering Cache-Vergiftung (target/, xwin-Ablage) low accept Der Cache-Server laeuft nur lokal fuer diesen Runner (172.18.0.1), keine fremden Schreiber; Schluessel haengt am Cargo.lock-Hash.
T-18-17 Repudiation Iterationsschleife low mitigate Jede Runde ist ein eigener Commit mit Ursache im Titel und im SUMMARY dokumentiert.
T-18-18 Information Disclosure Protokollauszuege (Token) low mitigate Gitea maskiert Secrets im Log; publish-release.sh gibt das Token nie aus (T-18-03).
T-18-SC Tampering cargo install --locked cargo-xwin (crates.io) low mitigate Legitimitaetspruefung in RESEARCH: OK (rust-cross/cargo-xwin, seit 2022, 63k Downloads/Woche); kein [ASSUMED]/[SUS], keine Sperr-Freigabe noetig.
</threat_model>
1. Statische Pruefung des Workflows (Kennzeichen, Reihenfolge AppImage vor NSIS, Einsammeln mit beiden Plattformen). 2. Gruener Pipeline-Lauf auf `main` (Rueckmeldung des Orchestrators) mit beiden Dateizeilen und gruenem `publish`. 3. Hoechstens drei dokumentierte Runden. 4. Nach dem Lauf traegt das Beta-Abbild die Pakete — sichtbar, sobald der Nutzer den Testserver auf den neuen Stand zieht (18-06).

<success_criteria>

  • Der Job desktop erzeugt auf dem Linux-Runner Tessera-Setup-X.Y.Z[…].exe und Tessera-X.Y.Z[…].AppImage in einem Lauf.
  • Der Lauf ist gruen; publish hat Beta-Abbilder mit Paketen gepusht.
  • Die Iterationsschleife ist dokumentiert und endete spaetestens nach drei Runden. </success_criteria>
Create `.planning/phases/18-desktop-client-fertigstellen/18-05-SUMMARY.md` when done. Im SUMMARY festhalten: Dauer des ersten und (falls vorhanden) eines zweiten Laufs mit warmem Cache, Groesse beider Dateien laut Sammel-Schritt, und die Tabelle der Runden (Signatur, Ursache, Aenderung, Commit).